roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux",
704                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052         }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059           
1060         // sort?? a.sort().equals(b.sort());
1061           
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064             return false;
1065             }
1066         }
1067         return true;
1068     } 
1069     
1070     
1071     
1072     
1073 });
1074
1075 Roo.applyIf(Array, {
1076  /**
1077      * from
1078      * @static
1079      * @param {Array} o Or Array like object (eg. nodelist)
1080      * @returns {Array} 
1081      */
1082     from : function(o)
1083     {
1084         var ret= [];
1085     
1086         for (var i =0; i < o.length; i++) { 
1087             ret[i] = o[i];
1088         }
1089         return ret;
1090       
1091     }
1092 });
1093 /*
1094  * Based on:
1095  * Ext JS Library 1.1.1
1096  * Copyright(c) 2006-2007, Ext JS, LLC.
1097  *
1098  * Originally Released Under LGPL - original licence link has changed is not relivant.
1099  *
1100  * Fork - LGPL
1101  * <script type="text/javascript">
1102  */
1103
1104 /**
1105  * @class Date
1106  *
1107  * The date parsing and format syntax is a subset of
1108  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1109  * supported will provide results equivalent to their PHP versions.
1110  *
1111  * Following is the list of all currently supported formats:
1112  *<pre>
1113 Sample date:
1114 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1115
1116 Format  Output      Description
1117 ------  ----------  --------------------------------------------------------------
1118   d      10         Day of the month, 2 digits with leading zeros
1119   D      Wed        A textual representation of a day, three letters
1120   j      10         Day of the month without leading zeros
1121   l      Wednesday  A full textual representation of the day of the week
1122   S      th         English ordinal day of month suffix, 2 chars (use with j)
1123   w      3          Numeric representation of the day of the week
1124   z      9          The julian date, or day of the year (0-365)
1125   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1126   F      January    A full textual representation of the month
1127   m      01         Numeric representation of a month, with leading zeros
1128   M      Jan        Month name abbreviation, three letters
1129   n      1          Numeric representation of a month, without leading zeros
1130   t      31         Number of days in the given month
1131   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1132   Y      2007       A full numeric representation of a year, 4 digits
1133   y      07         A two digit representation of a year
1134   a      pm         Lowercase Ante meridiem and Post meridiem
1135   A      PM         Uppercase Ante meridiem and Post meridiem
1136   g      3          12-hour format of an hour without leading zeros
1137   G      15         24-hour format of an hour without leading zeros
1138   h      03         12-hour format of an hour with leading zeros
1139   H      15         24-hour format of an hour with leading zeros
1140   i      05         Minutes with leading zeros
1141   s      01         Seconds, with leading zeros
1142   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1143   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1144   T      CST        Timezone setting of the machine running the code
1145   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1146 </pre>
1147  *
1148  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1149  * <pre><code>
1150 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1151 document.write(dt.format('Y-m-d'));                         //2007-01-10
1152 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1153 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
1154  </code></pre>
1155  *
1156  * Here are some standard date/time patterns that you might find helpful.  They
1157  * are not part of the source of Date.js, but to use them you can simply copy this
1158  * block of code into any script that is included after Date.js and they will also become
1159  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1160  * <pre><code>
1161 Date.patterns = {
1162     ISO8601Long:"Y-m-d H:i:s",
1163     ISO8601Short:"Y-m-d",
1164     ShortDate: "n/j/Y",
1165     LongDate: "l, F d, Y",
1166     FullDateTime: "l, F d, Y g:i:s A",
1167     MonthDay: "F d",
1168     ShortTime: "g:i A",
1169     LongTime: "g:i:s A",
1170     SortableDateTime: "Y-m-d\\TH:i:s",
1171     UniversalSortableDateTime: "Y-m-d H:i:sO",
1172     YearMonth: "F, Y"
1173 };
1174 </code></pre>
1175  *
1176  * Example usage:
1177  * <pre><code>
1178 var dt = new Date();
1179 document.write(dt.format(Date.patterns.ShortDate));
1180  </code></pre>
1181  */
1182
1183 /*
1184  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1185  * They generate precompiled functions from date formats instead of parsing and
1186  * processing the pattern every time you format a date.  These functions are available
1187  * on every Date object (any javascript function).
1188  *
1189  * The original article and download are here:
1190  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1191  *
1192  */
1193  
1194  
1195  // was in core
1196 /**
1197  Returns the number of milliseconds between this date and date
1198  @param {Date} date (optional) Defaults to now
1199  @return {Number} The diff in milliseconds
1200  @member Date getElapsed
1201  */
1202 Date.prototype.getElapsed = function(date) {
1203         return Math.abs((date || new Date()).getTime()-this.getTime());
1204 };
1205 // was in date file..
1206
1207
1208 // private
1209 Date.parseFunctions = {count:0};
1210 // private
1211 Date.parseRegexes = [];
1212 // private
1213 Date.formatFunctions = {count:0};
1214
1215 // private
1216 Date.prototype.dateFormat = function(format) {
1217     if (Date.formatFunctions[format] == null) {
1218         Date.createNewFormat(format);
1219     }
1220     var func = Date.formatFunctions[format];
1221     return this[func]();
1222 };
1223
1224
1225 /**
1226  * Formats a date given the supplied format string
1227  * @param {String} format The format string
1228  * @return {String} The formatted date
1229  * @method
1230  */
1231 Date.prototype.format = Date.prototype.dateFormat;
1232
1233 // private
1234 Date.createNewFormat = function(format) {
1235     var funcName = "format" + Date.formatFunctions.count++;
1236     Date.formatFunctions[format] = funcName;
1237     var code = "Date.prototype." + funcName + " = function(){return ";
1238     var special = false;
1239     var ch = '';
1240     for (var i = 0; i < format.length; ++i) {
1241         ch = format.charAt(i);
1242         if (!special && ch == "\\") {
1243             special = true;
1244         }
1245         else if (special) {
1246             special = false;
1247             code += "'" + String.escape(ch) + "' + ";
1248         }
1249         else {
1250             code += Date.getFormatCode(ch);
1251         }
1252     }
1253     /** eval:var:zzzzzzzzzzzzz */
1254     eval(code.substring(0, code.length - 3) + ";}");
1255 };
1256
1257 // private
1258 Date.getFormatCode = function(character) {
1259     switch (character) {
1260     case "d":
1261         return "String.leftPad(this.getDate(), 2, '0') + ";
1262     case "D":
1263         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1264     case "j":
1265         return "this.getDate() + ";
1266     case "l":
1267         return "Date.dayNames[this.getDay()] + ";
1268     case "S":
1269         return "this.getSuffix() + ";
1270     case "w":
1271         return "this.getDay() + ";
1272     case "z":
1273         return "this.getDayOfYear() + ";
1274     case "W":
1275         return "this.getWeekOfYear() + ";
1276     case "F":
1277         return "Date.monthNames[this.getMonth()] + ";
1278     case "m":
1279         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1280     case "M":
1281         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1282     case "n":
1283         return "(this.getMonth() + 1) + ";
1284     case "t":
1285         return "this.getDaysInMonth() + ";
1286     case "L":
1287         return "(this.isLeapYear() ? 1 : 0) + ";
1288     case "Y":
1289         return "this.getFullYear() + ";
1290     case "y":
1291         return "('' + this.getFullYear()).substring(2, 4) + ";
1292     case "a":
1293         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1294     case "A":
1295         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1296     case "g":
1297         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1298     case "G":
1299         return "this.getHours() + ";
1300     case "h":
1301         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1302     case "H":
1303         return "String.leftPad(this.getHours(), 2, '0') + ";
1304     case "i":
1305         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1306     case "s":
1307         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1308     case "O":
1309         return "this.getGMTOffset() + ";
1310     case "P":
1311         return "this.getGMTColonOffset() + ";
1312     case "T":
1313         return "this.getTimezone() + ";
1314     case "Z":
1315         return "(this.getTimezoneOffset() * -60) + ";
1316     default:
1317         return "'" + String.escape(character) + "' + ";
1318     }
1319 };
1320
1321 /**
1322  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1323  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1324  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1325  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1326  * string or the parse operation will fail.
1327  * Example Usage:
1328 <pre><code>
1329 //dt = Fri May 25 2007 (current date)
1330 var dt = new Date();
1331
1332 //dt = Thu May 25 2006 (today's month/day in 2006)
1333 dt = Date.parseDate("2006", "Y");
1334
1335 //dt = Sun Jan 15 2006 (all date parts specified)
1336 dt = Date.parseDate("2006-1-15", "Y-m-d");
1337
1338 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1339 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1340 </code></pre>
1341  * @param {String} input The unparsed date as a string
1342  * @param {String} format The format the date is in
1343  * @return {Date} The parsed date
1344  * @static
1345  */
1346 Date.parseDate = function(input, format) {
1347     if (Date.parseFunctions[format] == null) {
1348         Date.createParser(format);
1349     }
1350     var func = Date.parseFunctions[format];
1351     return Date[func](input);
1352 };
1353 /**
1354  * @private
1355  */
1356
1357 Date.createParser = function(format) {
1358     var funcName = "parse" + Date.parseFunctions.count++;
1359     var regexNum = Date.parseRegexes.length;
1360     var currentGroup = 1;
1361     Date.parseFunctions[format] = funcName;
1362
1363     var code = "Date." + funcName + " = function(input){\n"
1364         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1365         + "var d = new Date();\n"
1366         + "y = d.getFullYear();\n"
1367         + "m = d.getMonth();\n"
1368         + "d = d.getDate();\n"
1369         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1370         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1371         + "if (results && results.length > 0) {";
1372     var regex = "";
1373
1374     var special = false;
1375     var ch = '';
1376     for (var i = 0; i < format.length; ++i) {
1377         ch = format.charAt(i);
1378         if (!special && ch == "\\") {
1379             special = true;
1380         }
1381         else if (special) {
1382             special = false;
1383             regex += String.escape(ch);
1384         }
1385         else {
1386             var obj = Date.formatCodeToRegex(ch, currentGroup);
1387             currentGroup += obj.g;
1388             regex += obj.s;
1389             if (obj.g && obj.c) {
1390                 code += obj.c;
1391             }
1392         }
1393     }
1394
1395     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1396         + "{v = new Date(y, m, d, h, i, s);}\n"
1397         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1398         + "{v = new Date(y, m, d, h, i);}\n"
1399         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1400         + "{v = new Date(y, m, d, h);}\n"
1401         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1402         + "{v = new Date(y, m, d);}\n"
1403         + "else if (y >= 0 && m >= 0)\n"
1404         + "{v = new Date(y, m);}\n"
1405         + "else if (y >= 0)\n"
1406         + "{v = new Date(y);}\n"
1407         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1408         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1409         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1410         + ";}";
1411
1412     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1413     /** eval:var:zzzzzzzzzzzzz */
1414     eval(code);
1415 };
1416
1417 // private
1418 Date.formatCodeToRegex = function(character, currentGroup) {
1419     switch (character) {
1420     case "D":
1421         return {g:0,
1422         c:null,
1423         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1424     case "j":
1425         return {g:1,
1426             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1427             s:"(\\d{1,2})"}; // day of month without leading zeroes
1428     case "d":
1429         return {g:1,
1430             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1431             s:"(\\d{2})"}; // day of month with leading zeroes
1432     case "l":
1433         return {g:0,
1434             c:null,
1435             s:"(?:" + Date.dayNames.join("|") + ")"};
1436     case "S":
1437         return {g:0,
1438             c:null,
1439             s:"(?:st|nd|rd|th)"};
1440     case "w":
1441         return {g:0,
1442             c:null,
1443             s:"\\d"};
1444     case "z":
1445         return {g:0,
1446             c:null,
1447             s:"(?:\\d{1,3})"};
1448     case "W":
1449         return {g:0,
1450             c:null,
1451             s:"(?:\\d{2})"};
1452     case "F":
1453         return {g:1,
1454             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1455             s:"(" + Date.monthNames.join("|") + ")"};
1456     case "M":
1457         return {g:1,
1458             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1459             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1460     case "n":
1461         return {g:1,
1462             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1463             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1464     case "m":
1465         return {g:1,
1466             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1467             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1468     case "t":
1469         return {g:0,
1470             c:null,
1471             s:"\\d{1,2}"};
1472     case "L":
1473         return {g:0,
1474             c:null,
1475             s:"(?:1|0)"};
1476     case "Y":
1477         return {g:1,
1478             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1479             s:"(\\d{4})"};
1480     case "y":
1481         return {g:1,
1482             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1483                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1484             s:"(\\d{1,2})"};
1485     case "a":
1486         return {g:1,
1487             c:"if (results[" + currentGroup + "] == 'am') {\n"
1488                 + "if (h == 12) { h = 0; }\n"
1489                 + "} else { if (h < 12) { h += 12; }}",
1490             s:"(am|pm)"};
1491     case "A":
1492         return {g:1,
1493             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1494                 + "if (h == 12) { h = 0; }\n"
1495                 + "} else { if (h < 12) { h += 12; }}",
1496             s:"(AM|PM)"};
1497     case "g":
1498     case "G":
1499         return {g:1,
1500             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1501             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1502     case "h":
1503     case "H":
1504         return {g:1,
1505             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1506             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1507     case "i":
1508         return {g:1,
1509             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1510             s:"(\\d{2})"};
1511     case "s":
1512         return {g:1,
1513             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1514             s:"(\\d{2})"};
1515     case "O":
1516         return {g:1,
1517             c:[
1518                 "o = results[", currentGroup, "];\n",
1519                 "var sn = o.substring(0,1);\n", // get + / - sign
1520                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1521                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1522                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1523                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1524             ].join(""),
1525             s:"([+\-]\\d{2,4})"};
1526     
1527     
1528     case "P":
1529         return {g:1,
1530                 c:[
1531                    "o = results[", currentGroup, "];\n",
1532                    "var sn = o.substring(0,1);\n",
1533                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1534                    "var mn = o.substring(4,6) % 60;\n",
1535                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1536                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1537             ].join(""),
1538             s:"([+\-]\\d{4})"};
1539     case "T":
1540         return {g:0,
1541             c:null,
1542             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1543     case "Z":
1544         return {g:1,
1545             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1546                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1547             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1548     default:
1549         return {g:0,
1550             c:null,
1551             s:String.escape(character)};
1552     }
1553 };
1554
1555 /**
1556  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1557  * @return {String} The abbreviated timezone name (e.g. 'CST')
1558  */
1559 Date.prototype.getTimezone = function() {
1560     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1561 };
1562
1563 /**
1564  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1565  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1566  */
1567 Date.prototype.getGMTOffset = function() {
1568     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1569         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1570         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1571 };
1572
1573 /**
1574  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1575  * @return {String} 2-characters representing hours and 2-characters representing minutes
1576  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1577  */
1578 Date.prototype.getGMTColonOffset = function() {
1579         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1580                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1581                 + ":"
1582                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1583 }
1584
1585 /**
1586  * Get the numeric day number of the year, adjusted for leap year.
1587  * @return {Number} 0 through 364 (365 in leap years)
1588  */
1589 Date.prototype.getDayOfYear = function() {
1590     var num = 0;
1591     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1592     for (var i = 0; i < this.getMonth(); ++i) {
1593         num += Date.daysInMonth[i];
1594     }
1595     return num + this.getDate() - 1;
1596 };
1597
1598 /**
1599  * Get the string representation of the numeric week number of the year
1600  * (equivalent to the format specifier 'W').
1601  * @return {String} '00' through '52'
1602  */
1603 Date.prototype.getWeekOfYear = function() {
1604     // Skip to Thursday of this week
1605     var now = this.getDayOfYear() + (4 - this.getDay());
1606     // Find the first Thursday of the year
1607     var jan1 = new Date(this.getFullYear(), 0, 1);
1608     var then = (7 - jan1.getDay() + 4);
1609     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1610 };
1611
1612 /**
1613  * Whether or not the current date is in a leap year.
1614  * @return {Boolean} True if the current date is in a leap year, else false
1615  */
1616 Date.prototype.isLeapYear = function() {
1617     var year = this.getFullYear();
1618     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1619 };
1620
1621 /**
1622  * Get the first day of the current month, adjusted for leap year.  The returned value
1623  * is the numeric day index within the week (0-6) which can be used in conjunction with
1624  * the {@link #monthNames} array to retrieve the textual day name.
1625  * Example:
1626  *<pre><code>
1627 var dt = new Date('1/10/2007');
1628 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1629 </code></pre>
1630  * @return {Number} The day number (0-6)
1631  */
1632 Date.prototype.getFirstDayOfMonth = function() {
1633     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1634     return (day < 0) ? (day + 7) : day;
1635 };
1636
1637 /**
1638  * Get the last day of the current month, adjusted for leap year.  The returned value
1639  * is the numeric day index within the week (0-6) which can be used in conjunction with
1640  * the {@link #monthNames} array to retrieve the textual day name.
1641  * Example:
1642  *<pre><code>
1643 var dt = new Date('1/10/2007');
1644 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1645 </code></pre>
1646  * @return {Number} The day number (0-6)
1647  */
1648 Date.prototype.getLastDayOfMonth = function() {
1649     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1650     return (day < 0) ? (day + 7) : day;
1651 };
1652
1653
1654 /**
1655  * Get the first date of this date's month
1656  * @return {Date}
1657  */
1658 Date.prototype.getFirstDateOfMonth = function() {
1659     return new Date(this.getFullYear(), this.getMonth(), 1);
1660 };
1661
1662 /**
1663  * Get the last date of this date's month
1664  * @return {Date}
1665  */
1666 Date.prototype.getLastDateOfMonth = function() {
1667     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1668 };
1669 /**
1670  * Get the number of days in the current month, adjusted for leap year.
1671  * @return {Number} The number of days in the month
1672  */
1673 Date.prototype.getDaysInMonth = function() {
1674     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1675     return Date.daysInMonth[this.getMonth()];
1676 };
1677
1678 /**
1679  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1680  * @return {String} 'st, 'nd', 'rd' or 'th'
1681  */
1682 Date.prototype.getSuffix = function() {
1683     switch (this.getDate()) {
1684         case 1:
1685         case 21:
1686         case 31:
1687             return "st";
1688         case 2:
1689         case 22:
1690             return "nd";
1691         case 3:
1692         case 23:
1693             return "rd";
1694         default:
1695             return "th";
1696     }
1697 };
1698
1699 // private
1700 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1701
1702 /**
1703  * An array of textual month names.
1704  * Override these values for international dates, for example...
1705  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1706  * @type Array
1707  * @static
1708  */
1709 Date.monthNames =
1710    ["January",
1711     "February",
1712     "March",
1713     "April",
1714     "May",
1715     "June",
1716     "July",
1717     "August",
1718     "September",
1719     "October",
1720     "November",
1721     "December"];
1722
1723 /**
1724  * An array of textual day names.
1725  * Override these values for international dates, for example...
1726  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1727  * @type Array
1728  * @static
1729  */
1730 Date.dayNames =
1731    ["Sunday",
1732     "Monday",
1733     "Tuesday",
1734     "Wednesday",
1735     "Thursday",
1736     "Friday",
1737     "Saturday"];
1738
1739 // private
1740 Date.y2kYear = 50;
1741 // private
1742 Date.monthNumbers = {
1743     Jan:0,
1744     Feb:1,
1745     Mar:2,
1746     Apr:3,
1747     May:4,
1748     Jun:5,
1749     Jul:6,
1750     Aug:7,
1751     Sep:8,
1752     Oct:9,
1753     Nov:10,
1754     Dec:11};
1755
1756 /**
1757  * Creates and returns a new Date instance with the exact same date value as the called instance.
1758  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1759  * variable will also be changed.  When the intention is to create a new variable that will not
1760  * modify the original instance, you should create a clone.
1761  *
1762  * Example of correctly cloning a date:
1763  * <pre><code>
1764 //wrong way:
1765 var orig = new Date('10/1/2006');
1766 var copy = orig;
1767 copy.setDate(5);
1768 document.write(orig);  //returns 'Thu Oct 05 2006'!
1769
1770 //correct way:
1771 var orig = new Date('10/1/2006');
1772 var copy = orig.clone();
1773 copy.setDate(5);
1774 document.write(orig);  //returns 'Thu Oct 01 2006'
1775 </code></pre>
1776  * @return {Date} The new Date instance
1777  */
1778 Date.prototype.clone = function() {
1779         return new Date(this.getTime());
1780 };
1781
1782 /**
1783  * Clears any time information from this date
1784  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1785  @return {Date} this or the clone
1786  */
1787 Date.prototype.clearTime = function(clone){
1788     if(clone){
1789         return this.clone().clearTime();
1790     }
1791     this.setHours(0);
1792     this.setMinutes(0);
1793     this.setSeconds(0);
1794     this.setMilliseconds(0);
1795     return this;
1796 };
1797
1798 // private
1799 // safari setMonth is broken -- check that this is only donw once...
1800 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1801     Date.brokenSetMonth = Date.prototype.setMonth;
1802         Date.prototype.setMonth = function(num){
1803                 if(num <= -1){
1804                         var n = Math.ceil(-num);
1805                         var back_year = Math.ceil(n/12);
1806                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1807                         this.setFullYear(this.getFullYear() - back_year);
1808                         return Date.brokenSetMonth.call(this, month);
1809                 } else {
1810                         return Date.brokenSetMonth.apply(this, arguments);
1811                 }
1812         };
1813 }
1814
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MILLI = "ms";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.SECOND = "s";
1823 /** Date interval constant 
1824 * @static 
1825 * @type String */
1826 Date.MINUTE = "mi";
1827 /** Date interval constant 
1828 * @static 
1829 * @type String */
1830 Date.HOUR = "h";
1831 /** Date interval constant 
1832 * @static 
1833 * @type String */
1834 Date.DAY = "d";
1835 /** Date interval constant 
1836 * @static 
1837 * @type String */
1838 Date.MONTH = "mo";
1839 /** Date interval constant 
1840 * @static 
1841 * @type String */
1842 Date.YEAR = "y";
1843
1844 /**
1845  * Provides a convenient method of performing basic date arithmetic.  This method
1846  * does not modify the Date instance being called - it creates and returns
1847  * a new Date instance containing the resulting date value.
1848  *
1849  * Examples:
1850  * <pre><code>
1851 //Basic usage:
1852 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1853 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1854
1855 //Negative values will subtract correctly:
1856 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1857 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1858
1859 //You can even chain several calls together in one line!
1860 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1861 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1862  </code></pre>
1863  *
1864  * @param {String} interval   A valid date interval enum value
1865  * @param {Number} value      The amount to add to the current date
1866  * @return {Date} The new Date instance
1867  */
1868 Date.prototype.add = function(interval, value){
1869   var d = this.clone();
1870   if (!interval || value === 0) { return d; }
1871   switch(interval.toLowerCase()){
1872     case Date.MILLI:
1873       d.setMilliseconds(this.getMilliseconds() + value);
1874       break;
1875     case Date.SECOND:
1876       d.setSeconds(this.getSeconds() + value);
1877       break;
1878     case Date.MINUTE:
1879       d.setMinutes(this.getMinutes() + value);
1880       break;
1881     case Date.HOUR:
1882       d.setHours(this.getHours() + value);
1883       break;
1884     case Date.DAY:
1885       d.setDate(this.getDate() + value);
1886       break;
1887     case Date.MONTH:
1888       var day = this.getDate();
1889       if(day > 28){
1890           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1891       }
1892       d.setDate(day);
1893       d.setMonth(this.getMonth() + value);
1894       break;
1895     case Date.YEAR:
1896       d.setFullYear(this.getFullYear() + value);
1897       break;
1898   }
1899   return d;
1900 };
1901 /**
1902  * @class Roo.lib.Dom
1903  * @licence LGPL
1904  * @static
1905  * 
1906  * Dom utils (from YIU afaik)
1907  *
1908  * 
1909  **/
1910 Roo.lib.Dom = {
1911     /**
1912      * Get the view width
1913      * @param {Boolean} full True will get the full document, otherwise it's the view width
1914      * @return {Number} The width
1915      */
1916      
1917     getViewWidth : function(full) {
1918         return full ? this.getDocumentWidth() : this.getViewportWidth();
1919     },
1920     /**
1921      * Get the view height
1922      * @param {Boolean} full True will get the full document, otherwise it's the view height
1923      * @return {Number} The height
1924      */
1925     getViewHeight : function(full) {
1926         return full ? this.getDocumentHeight() : this.getViewportHeight();
1927     },
1928     /**
1929      * Get the Full Document height 
1930      * @return {Number} The height
1931      */
1932     getDocumentHeight: function() {
1933         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1934         return Math.max(scrollHeight, this.getViewportHeight());
1935     },
1936     /**
1937      * Get the Full Document width
1938      * @return {Number} The width
1939      */
1940     getDocumentWidth: function() {
1941         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1942         return Math.max(scrollWidth, this.getViewportWidth());
1943     },
1944     /**
1945      * Get the Window Viewport height
1946      * @return {Number} The height
1947      */
1948     getViewportHeight: function() {
1949         var height = self.innerHeight;
1950         var mode = document.compatMode;
1951
1952         if ((mode || Roo.isIE) && !Roo.isOpera) {
1953             height = (mode == "CSS1Compat") ?
1954                      document.documentElement.clientHeight :
1955                      document.body.clientHeight;
1956         }
1957
1958         return height;
1959     },
1960     /**
1961      * Get the Window Viewport width
1962      * @return {Number} The width
1963      */
1964     getViewportWidth: function() {
1965         var width = self.innerWidth;
1966         var mode = document.compatMode;
1967
1968         if (mode || Roo.isIE) {
1969             width = (mode == "CSS1Compat") ?
1970                     document.documentElement.clientWidth :
1971                     document.body.clientWidth;
1972         }
1973         return width;
1974     },
1975
1976     isAncestor : function(p, c) {
1977         p = Roo.getDom(p);
1978         c = Roo.getDom(c);
1979         if (!p || !c) {
1980             return false;
1981         }
1982
1983         if (p.contains && !Roo.isSafari) {
1984             return p.contains(c);
1985         } else if (p.compareDocumentPosition) {
1986             return !!(p.compareDocumentPosition(c) & 16);
1987         } else {
1988             var parent = c.parentNode;
1989             while (parent) {
1990                 if (parent == p) {
1991                     return true;
1992                 }
1993                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1994                     return false;
1995                 }
1996                 parent = parent.parentNode;
1997             }
1998             return false;
1999         }
2000     },
2001
2002     getRegion : function(el) {
2003         return Roo.lib.Region.getRegion(el);
2004     },
2005
2006     getY : function(el) {
2007         return this.getXY(el)[1];
2008     },
2009
2010     getX : function(el) {
2011         return this.getXY(el)[0];
2012     },
2013
2014     getXY : function(el) {
2015         var p, pe, b, scroll, bd = document.body;
2016         el = Roo.getDom(el);
2017         var fly = Roo.lib.AnimBase.fly;
2018         if (el.getBoundingClientRect) {
2019             b = el.getBoundingClientRect();
2020             scroll = fly(document).getScroll();
2021             return [b.left + scroll.left, b.top + scroll.top];
2022         }
2023         var x = 0, y = 0;
2024
2025         p = el;
2026
2027         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2028
2029         while (p) {
2030
2031             x += p.offsetLeft;
2032             y += p.offsetTop;
2033
2034             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2035                 hasAbsolute = true;
2036             }
2037
2038             if (Roo.isGecko) {
2039                 pe = fly(p);
2040
2041                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2042                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2043
2044
2045                 x += bl;
2046                 y += bt;
2047
2048
2049                 if (p != el && pe.getStyle('overflow') != 'visible') {
2050                     x += bl;
2051                     y += bt;
2052                 }
2053             }
2054             p = p.offsetParent;
2055         }
2056
2057         if (Roo.isSafari && hasAbsolute) {
2058             x -= bd.offsetLeft;
2059             y -= bd.offsetTop;
2060         }
2061
2062         if (Roo.isGecko && !hasAbsolute) {
2063             var dbd = fly(bd);
2064             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2065             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2066         }
2067
2068         p = el.parentNode;
2069         while (p && p != bd) {
2070             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2071                 x -= p.scrollLeft;
2072                 y -= p.scrollTop;
2073             }
2074             p = p.parentNode;
2075         }
2076         return [x, y];
2077     },
2078  
2079   
2080
2081
2082     setXY : function(el, xy) {
2083         el = Roo.fly(el, '_setXY');
2084         el.position();
2085         var pts = el.translatePoints(xy);
2086         if (xy[0] !== false) {
2087             el.dom.style.left = pts.left + "px";
2088         }
2089         if (xy[1] !== false) {
2090             el.dom.style.top = pts.top + "px";
2091         }
2092     },
2093
2094     setX : function(el, x) {
2095         this.setXY(el, [x, false]);
2096     },
2097
2098     setY : function(el, y) {
2099         this.setXY(el, [false, y]);
2100     }
2101 };
2102 /*
2103  * Portions of this file are based on pieces of Yahoo User Interface Library
2104  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2105  * YUI licensed under the BSD License:
2106  * http://developer.yahoo.net/yui/license.txt
2107  * <script type="text/javascript">
2108  *
2109  */
2110
2111 Roo.lib.Event = function() {
2112     var loadComplete = false;
2113     var listeners = [];
2114     var unloadListeners = [];
2115     var retryCount = 0;
2116     var onAvailStack = [];
2117     var counter = 0;
2118     var lastError = null;
2119
2120     return {
2121         POLL_RETRYS: 200,
2122         POLL_INTERVAL: 20,
2123         EL: 0,
2124         TYPE: 1,
2125         FN: 2,
2126         WFN: 3,
2127         OBJ: 3,
2128         ADJ_SCOPE: 4,
2129         _interval: null,
2130
2131         startInterval: function() {
2132             if (!this._interval) {
2133                 var self = this;
2134                 var callback = function() {
2135                     self._tryPreloadAttach();
2136                 };
2137                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2138
2139             }
2140         },
2141
2142         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2143             onAvailStack.push({ id:         p_id,
2144                 fn:         p_fn,
2145                 obj:        p_obj,
2146                 override:   p_override,
2147                 checkReady: false    });
2148
2149             retryCount = this.POLL_RETRYS;
2150             this.startInterval();
2151         },
2152
2153
2154         addListener: function(el, eventName, fn) {
2155             el = Roo.getDom(el);
2156             if (!el || !fn) {
2157                 return false;
2158             }
2159
2160             if ("unload" == eventName) {
2161                 unloadListeners[unloadListeners.length] =
2162                 [el, eventName, fn];
2163                 return true;
2164             }
2165
2166             var wrappedFn = function(e) {
2167                 return fn(Roo.lib.Event.getEvent(e));
2168             };
2169
2170             var li = [el, eventName, fn, wrappedFn];
2171
2172             var index = listeners.length;
2173             listeners[index] = li;
2174
2175             this.doAdd(el, eventName, wrappedFn, false);
2176             return true;
2177
2178         },
2179
2180
2181         removeListener: function(el, eventName, fn) {
2182             var i, len;
2183
2184             el = Roo.getDom(el);
2185
2186             if(!fn) {
2187                 return this.purgeElement(el, false, eventName);
2188             }
2189
2190
2191             if ("unload" == eventName) {
2192
2193                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2194                     var li = unloadListeners[i];
2195                     if (li &&
2196                         li[0] == el &&
2197                         li[1] == eventName &&
2198                         li[2] == fn) {
2199                         unloadListeners.splice(i, 1);
2200                         return true;
2201                     }
2202                 }
2203
2204                 return false;
2205             }
2206
2207             var cacheItem = null;
2208
2209
2210             var index = arguments[3];
2211
2212             if ("undefined" == typeof index) {
2213                 index = this._getCacheIndex(el, eventName, fn);
2214             }
2215
2216             if (index >= 0) {
2217                 cacheItem = listeners[index];
2218             }
2219
2220             if (!el || !cacheItem) {
2221                 return false;
2222             }
2223
2224             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2225
2226             delete listeners[index][this.WFN];
2227             delete listeners[index][this.FN];
2228             listeners.splice(index, 1);
2229
2230             return true;
2231
2232         },
2233
2234
2235         getTarget: function(ev, resolveTextNode) {
2236             ev = ev.browserEvent || ev;
2237             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2238             var t = ev.target || ev.srcElement;
2239             return this.resolveTextNode(t);
2240         },
2241
2242
2243         resolveTextNode: function(node) {
2244             if (Roo.isSafari && node && 3 == node.nodeType) {
2245                 return node.parentNode;
2246             } else {
2247                 return node;
2248             }
2249         },
2250
2251
2252         getPageX: function(ev) {
2253             ev = ev.browserEvent || ev;
2254             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2255             var x = ev.pageX;
2256             if (!x && 0 !== x) {
2257                 x = ev.clientX || 0;
2258
2259                 if (Roo.isIE) {
2260                     x += this.getScroll()[1];
2261                 }
2262             }
2263
2264             return x;
2265         },
2266
2267
2268         getPageY: function(ev) {
2269             ev = ev.browserEvent || ev;
2270             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2271             var y = ev.pageY;
2272             if (!y && 0 !== y) {
2273                 y = ev.clientY || 0;
2274
2275                 if (Roo.isIE) {
2276                     y += this.getScroll()[0];
2277                 }
2278             }
2279
2280
2281             return y;
2282         },
2283
2284
2285         getXY: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2288             return [this.getPageX(ev), this.getPageY(ev)];
2289         },
2290
2291
2292         getRelatedTarget: function(ev) {
2293             ev = ev.browserEvent || ev;
2294             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2295             var t = ev.relatedTarget;
2296             if (!t) {
2297                 if (ev.type == "mouseout") {
2298                     t = ev.toElement;
2299                 } else if (ev.type == "mouseover") {
2300                     t = ev.fromElement;
2301                 }
2302             }
2303
2304             return this.resolveTextNode(t);
2305         },
2306
2307
2308         getTime: function(ev) {
2309             ev = ev.browserEvent || ev;
2310             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2311             if (!ev.time) {
2312                 var t = new Date().getTime();
2313                 try {
2314                     ev.time = t;
2315                 } catch(ex) {
2316                     this.lastError = ex;
2317                     return t;
2318                 }
2319             }
2320
2321             return ev.time;
2322         },
2323
2324
2325         stopEvent: function(ev) {
2326             this.stopPropagation(ev);
2327             this.preventDefault(ev);
2328         },
2329
2330
2331         stopPropagation: function(ev) {
2332             ev = ev.browserEvent || ev;
2333             if (ev.stopPropagation) {
2334                 ev.stopPropagation();
2335             } else {
2336                 ev.cancelBubble = true;
2337             }
2338         },
2339
2340
2341         preventDefault: function(ev) {
2342             ev = ev.browserEvent || ev;
2343             if(ev.preventDefault) {
2344                 ev.preventDefault();
2345             } else {
2346                 ev.returnValue = false;
2347             }
2348         },
2349
2350
2351         getEvent: function(e) {
2352             var ev = e || window.event;
2353             if (!ev) {
2354                 var c = this.getEvent.caller;
2355                 while (c) {
2356                     ev = c.arguments[0];
2357                     if (ev && Event == ev.constructor) {
2358                         break;
2359                     }
2360                     c = c.caller;
2361                 }
2362             }
2363             return ev;
2364         },
2365
2366
2367         getCharCode: function(ev) {
2368             ev = ev.browserEvent || ev;
2369             return ev.charCode || ev.keyCode || 0;
2370         },
2371
2372
2373         _getCacheIndex: function(el, eventName, fn) {
2374             for (var i = 0,len = listeners.length; i < len; ++i) {
2375                 var li = listeners[i];
2376                 if (li &&
2377                     li[this.FN] == fn &&
2378                     li[this.EL] == el &&
2379                     li[this.TYPE] == eventName) {
2380                     return i;
2381                 }
2382             }
2383
2384             return -1;
2385         },
2386
2387
2388         elCache: {},
2389
2390
2391         getEl: function(id) {
2392             return document.getElementById(id);
2393         },
2394
2395
2396         clearCache: function() {
2397         },
2398
2399
2400         _load: function(e) {
2401             loadComplete = true;
2402             var EU = Roo.lib.Event;
2403
2404
2405             if (Roo.isIE) {
2406                 EU.doRemove(window, "load", EU._load);
2407             }
2408         },
2409
2410
2411         _tryPreloadAttach: function() {
2412
2413             if (this.locked) {
2414                 return false;
2415             }
2416
2417             this.locked = true;
2418
2419
2420             var tryAgain = !loadComplete;
2421             if (!tryAgain) {
2422                 tryAgain = (retryCount > 0);
2423             }
2424
2425
2426             var notAvail = [];
2427             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2428                 var item = onAvailStack[i];
2429                 if (item) {
2430                     var el = this.getEl(item.id);
2431
2432                     if (el) {
2433                         if (!item.checkReady ||
2434                             loadComplete ||
2435                             el.nextSibling ||
2436                             (document && document.body)) {
2437
2438                             var scope = el;
2439                             if (item.override) {
2440                                 if (item.override === true) {
2441                                     scope = item.obj;
2442                                 } else {
2443                                     scope = item.override;
2444                                 }
2445                             }
2446                             item.fn.call(scope, item.obj);
2447                             onAvailStack[i] = null;
2448                         }
2449                     } else {
2450                         notAvail.push(item);
2451                     }
2452                 }
2453             }
2454
2455             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2456
2457             if (tryAgain) {
2458
2459                 this.startInterval();
2460             } else {
2461                 clearInterval(this._interval);
2462                 this._interval = null;
2463             }
2464
2465             this.locked = false;
2466
2467             return true;
2468
2469         },
2470
2471
2472         purgeElement: function(el, recurse, eventName) {
2473             var elListeners = this.getListeners(el, eventName);
2474             if (elListeners) {
2475                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2476                     var l = elListeners[i];
2477                     this.removeListener(el, l.type, l.fn);
2478                 }
2479             }
2480
2481             if (recurse && el && el.childNodes) {
2482                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2483                     this.purgeElement(el.childNodes[i], recurse, eventName);
2484                 }
2485             }
2486         },
2487
2488
2489         getListeners: function(el, eventName) {
2490             var results = [], searchLists;
2491             if (!eventName) {
2492                 searchLists = [listeners, unloadListeners];
2493             } else if (eventName == "unload") {
2494                 searchLists = [unloadListeners];
2495             } else {
2496                 searchLists = [listeners];
2497             }
2498
2499             for (var j = 0; j < searchLists.length; ++j) {
2500                 var searchList = searchLists[j];
2501                 if (searchList && searchList.length > 0) {
2502                     for (var i = 0,len = searchList.length; i < len; ++i) {
2503                         var l = searchList[i];
2504                         if (l && l[this.EL] === el &&
2505                             (!eventName || eventName === l[this.TYPE])) {
2506                             results.push({
2507                                 type:   l[this.TYPE],
2508                                 fn:     l[this.FN],
2509                                 obj:    l[this.OBJ],
2510                                 adjust: l[this.ADJ_SCOPE],
2511                                 index:  i
2512                             });
2513                         }
2514                     }
2515                 }
2516             }
2517
2518             return (results.length) ? results : null;
2519         },
2520
2521
2522         _unload: function(e) {
2523
2524             var EU = Roo.lib.Event, i, j, l, len, index;
2525
2526             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2527                 l = unloadListeners[i];
2528                 if (l) {
2529                     var scope = window;
2530                     if (l[EU.ADJ_SCOPE]) {
2531                         if (l[EU.ADJ_SCOPE] === true) {
2532                             scope = l[EU.OBJ];
2533                         } else {
2534                             scope = l[EU.ADJ_SCOPE];
2535                         }
2536                     }
2537                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2538                     unloadListeners[i] = null;
2539                     l = null;
2540                     scope = null;
2541                 }
2542             }
2543
2544             unloadListeners = null;
2545
2546             if (listeners && listeners.length > 0) {
2547                 j = listeners.length;
2548                 while (j) {
2549                     index = j - 1;
2550                     l = listeners[index];
2551                     if (l) {
2552                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2553                                 l[EU.FN], index);
2554                     }
2555                     j = j - 1;
2556                 }
2557                 l = null;
2558
2559                 EU.clearCache();
2560             }
2561
2562             EU.doRemove(window, "unload", EU._unload);
2563
2564         },
2565
2566
2567         getScroll: function() {
2568             var dd = document.documentElement, db = document.body;
2569             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2570                 return [dd.scrollTop, dd.scrollLeft];
2571             } else if (db) {
2572                 return [db.scrollTop, db.scrollLeft];
2573             } else {
2574                 return [0, 0];
2575             }
2576         },
2577
2578
2579         doAdd: function () {
2580             if (window.addEventListener) {
2581                 return function(el, eventName, fn, capture) {
2582                     el.addEventListener(eventName, fn, (capture));
2583                 };
2584             } else if (window.attachEvent) {
2585                 return function(el, eventName, fn, capture) {
2586                     el.attachEvent("on" + eventName, fn);
2587                 };
2588             } else {
2589                 return function() {
2590                 };
2591             }
2592         }(),
2593
2594
2595         doRemove: function() {
2596             if (window.removeEventListener) {
2597                 return function (el, eventName, fn, capture) {
2598                     el.removeEventListener(eventName, fn, (capture));
2599                 };
2600             } else if (window.detachEvent) {
2601                 return function (el, eventName, fn) {
2602                     el.detachEvent("on" + eventName, fn);
2603                 };
2604             } else {
2605                 return function() {
2606                 };
2607             }
2608         }()
2609     };
2610     
2611 }();
2612 (function() {     
2613    
2614     var E = Roo.lib.Event;
2615     E.on = E.addListener;
2616     E.un = E.removeListener;
2617
2618     if (document && document.body) {
2619         E._load();
2620     } else {
2621         E.doAdd(window, "load", E._load);
2622     }
2623     E.doAdd(window, "unload", E._unload);
2624     E._tryPreloadAttach();
2625 })();
2626
2627  
2628
2629 (function() {
2630     /**
2631      * @class Roo.lib.Ajax
2632      *
2633      * provide a simple Ajax request utility functions
2634      * 
2635      * Portions of this file are based on pieces of Yahoo User Interface Library
2636     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2637     * YUI licensed under the BSD License:
2638     * http://developer.yahoo.net/yui/license.txt
2639     * <script type="text/javascript">
2640     *
2641      *
2642      */
2643     Roo.lib.Ajax = {
2644         /**
2645          * @static 
2646          */
2647         request : function(method, uri, cb, data, options) {
2648             if(options){
2649                 var hs = options.headers;
2650                 if(hs){
2651                     for(var h in hs){
2652                         if(hs.hasOwnProperty(h)){
2653                             this.initHeader(h, hs[h], false);
2654                         }
2655                     }
2656                 }
2657                 if(options.xmlData){
2658                     this.initHeader('Content-Type', 'text/xml', false);
2659                     method = 'POST';
2660                     data = options.xmlData;
2661                 }
2662             }
2663
2664             return this.asyncRequest(method, uri, cb, data);
2665         },
2666         /**
2667          * serialize a form
2668          *
2669          * @static
2670          * @param {DomForm} form element
2671          * @return {String} urlencode form output.
2672          */
2673         serializeForm : function(form) {
2674             if(typeof form == 'string') {
2675                 form = (document.getElementById(form) || document.forms[form]);
2676             }
2677
2678             var el, name, val, disabled, data = '', hasSubmit = false;
2679             for (var i = 0; i < form.elements.length; i++) {
2680                 el = form.elements[i];
2681                 disabled = form.elements[i].disabled;
2682                 name = form.elements[i].name;
2683                 val = form.elements[i].value;
2684
2685                 if (!disabled && name){
2686                     switch (el.type)
2687                             {
2688                         case 'select-one':
2689                         case 'select-multiple':
2690                             for (var j = 0; j < el.options.length; j++) {
2691                                 if (el.options[j].selected) {
2692                                     if (Roo.isIE) {
2693                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2694                                     }
2695                                     else {
2696                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2697                                     }
2698                                 }
2699                             }
2700                             break;
2701                         case 'radio':
2702                         case 'checkbox':
2703                             if (el.checked) {
2704                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2705                             }
2706                             break;
2707                         case 'file':
2708
2709                         case undefined:
2710
2711                         case 'reset':
2712
2713                         case 'button':
2714
2715                             break;
2716                         case 'submit':
2717                             if(hasSubmit == false) {
2718                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2719                                 hasSubmit = true;
2720                             }
2721                             break;
2722                         default:
2723                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2724                             break;
2725                     }
2726                 }
2727             }
2728             data = data.substr(0, data.length - 1);
2729             return data;
2730         },
2731
2732         headers:{},
2733
2734         hasHeaders:false,
2735
2736         useDefaultHeader:true,
2737
2738         defaultPostHeader:'application/x-www-form-urlencoded',
2739
2740         useDefaultXhrHeader:true,
2741
2742         defaultXhrHeader:'XMLHttpRequest',
2743
2744         hasDefaultHeaders:true,
2745
2746         defaultHeaders:{},
2747
2748         poll:{},
2749
2750         timeout:{},
2751
2752         pollInterval:50,
2753
2754         transactionId:0,
2755
2756         setProgId:function(id)
2757         {
2758             this.activeX.unshift(id);
2759         },
2760
2761         setDefaultPostHeader:function(b)
2762         {
2763             this.useDefaultHeader = b;
2764         },
2765
2766         setDefaultXhrHeader:function(b)
2767         {
2768             this.useDefaultXhrHeader = b;
2769         },
2770
2771         setPollingInterval:function(i)
2772         {
2773             if (typeof i == 'number' && isFinite(i)) {
2774                 this.pollInterval = i;
2775             }
2776         },
2777
2778         createXhrObject:function(transactionId)
2779         {
2780             var obj,http;
2781             try
2782             {
2783
2784                 http = new XMLHttpRequest();
2785
2786                 obj = { conn:http, tId:transactionId };
2787             }
2788             catch(e)
2789             {
2790                 for (var i = 0; i < this.activeX.length; ++i) {
2791                     try
2792                     {
2793
2794                         http = new ActiveXObject(this.activeX[i]);
2795
2796                         obj = { conn:http, tId:transactionId };
2797                         break;
2798                     }
2799                     catch(e) {
2800                     }
2801                 }
2802             }
2803             finally
2804             {
2805                 return obj;
2806             }
2807         },
2808
2809         getConnectionObject:function()
2810         {
2811             var o;
2812             var tId = this.transactionId;
2813
2814             try
2815             {
2816                 o = this.createXhrObject(tId);
2817                 if (o) {
2818                     this.transactionId++;
2819                 }
2820             }
2821             catch(e) {
2822             }
2823             finally
2824             {
2825                 return o;
2826             }
2827         },
2828
2829         asyncRequest:function(method, uri, callback, postData)
2830         {
2831             var o = this.getConnectionObject();
2832
2833             if (!o) {
2834                 return null;
2835             }
2836             else {
2837                 o.conn.open(method, uri, true);
2838
2839                 if (this.useDefaultXhrHeader) {
2840                     if (!this.defaultHeaders['X-Requested-With']) {
2841                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2842                     }
2843                 }
2844
2845                 if(postData && this.useDefaultHeader){
2846                     this.initHeader('Content-Type', this.defaultPostHeader);
2847                 }
2848
2849                  if (this.hasDefaultHeaders || this.hasHeaders) {
2850                     this.setHeader(o);
2851                 }
2852
2853                 this.handleReadyState(o, callback);
2854                 o.conn.send(postData || null);
2855
2856                 return o;
2857             }
2858         },
2859
2860         handleReadyState:function(o, callback)
2861         {
2862             var oConn = this;
2863
2864             if (callback && callback.timeout) {
2865                 
2866                 this.timeout[o.tId] = window.setTimeout(function() {
2867                     oConn.abort(o, callback, true);
2868                 }, callback.timeout);
2869             }
2870
2871             this.poll[o.tId] = window.setInterval(
2872                     function() {
2873                         if (o.conn && o.conn.readyState == 4) {
2874                             window.clearInterval(oConn.poll[o.tId]);
2875                             delete oConn.poll[o.tId];
2876
2877                             if(callback && callback.timeout) {
2878                                 window.clearTimeout(oConn.timeout[o.tId]);
2879                                 delete oConn.timeout[o.tId];
2880                             }
2881
2882                             oConn.handleTransactionResponse(o, callback);
2883                         }
2884                     }
2885                     , this.pollInterval);
2886         },
2887
2888         handleTransactionResponse:function(o, callback, isAbort)
2889         {
2890
2891             if (!callback) {
2892                 this.releaseObject(o);
2893                 return;
2894             }
2895
2896             var httpStatus, responseObject;
2897
2898             try
2899             {
2900                 if (o.conn.status !== undefined && o.conn.status != 0) {
2901                     httpStatus = o.conn.status;
2902                 }
2903                 else {
2904                     httpStatus = 13030;
2905                 }
2906             }
2907             catch(e) {
2908
2909
2910                 httpStatus = 13030;
2911             }
2912
2913             if (httpStatus >= 200 && httpStatus < 300) {
2914                 responseObject = this.createResponseObject(o, callback.argument);
2915                 if (callback.success) {
2916                     if (!callback.scope) {
2917                         callback.success(responseObject);
2918                     }
2919                     else {
2920
2921
2922                         callback.success.apply(callback.scope, [responseObject]);
2923                     }
2924                 }
2925             }
2926             else {
2927                 switch (httpStatus) {
2928
2929                     case 12002:
2930                     case 12029:
2931                     case 12030:
2932                     case 12031:
2933                     case 12152:
2934                     case 13030:
2935                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2936                         if (callback.failure) {
2937                             if (!callback.scope) {
2938                                 callback.failure(responseObject);
2939                             }
2940                             else {
2941                                 callback.failure.apply(callback.scope, [responseObject]);
2942                             }
2943                         }
2944                         break;
2945                     default:
2946                         responseObject = this.createResponseObject(o, callback.argument);
2947                         if (callback.failure) {
2948                             if (!callback.scope) {
2949                                 callback.failure(responseObject);
2950                             }
2951                             else {
2952                                 callback.failure.apply(callback.scope, [responseObject]);
2953                             }
2954                         }
2955                 }
2956             }
2957
2958             this.releaseObject(o);
2959             responseObject = null;
2960         },
2961
2962         createResponseObject:function(o, callbackArg)
2963         {
2964             var obj = {};
2965             var headerObj = {};
2966
2967             try
2968             {
2969                 var headerStr = o.conn.getAllResponseHeaders();
2970                 var header = headerStr.split('\n');
2971                 for (var i = 0; i < header.length; i++) {
2972                     var delimitPos = header[i].indexOf(':');
2973                     if (delimitPos != -1) {
2974                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2975                     }
2976                 }
2977             }
2978             catch(e) {
2979             }
2980
2981             obj.tId = o.tId;
2982             obj.status = o.conn.status;
2983             obj.statusText = o.conn.statusText;
2984             obj.getResponseHeader = headerObj;
2985             obj.getAllResponseHeaders = headerStr;
2986             obj.responseText = o.conn.responseText;
2987             obj.responseXML = o.conn.responseXML;
2988
2989             if (typeof callbackArg !== undefined) {
2990                 obj.argument = callbackArg;
2991             }
2992
2993             return obj;
2994         },
2995
2996         createExceptionObject:function(tId, callbackArg, isAbort)
2997         {
2998             var COMM_CODE = 0;
2999             var COMM_ERROR = 'communication failure';
3000             var ABORT_CODE = -1;
3001             var ABORT_ERROR = 'transaction aborted';
3002
3003             var obj = {};
3004
3005             obj.tId = tId;
3006             if (isAbort) {
3007                 obj.status = ABORT_CODE;
3008                 obj.statusText = ABORT_ERROR;
3009             }
3010             else {
3011                 obj.status = COMM_CODE;
3012                 obj.statusText = COMM_ERROR;
3013             }
3014
3015             if (callbackArg) {
3016                 obj.argument = callbackArg;
3017             }
3018
3019             return obj;
3020         },
3021
3022         initHeader:function(label, value, isDefault)
3023         {
3024             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3025
3026             if (headerObj[label] === undefined) {
3027                 headerObj[label] = value;
3028             }
3029             else {
3030
3031
3032                 headerObj[label] = value + "," + headerObj[label];
3033             }
3034
3035             if (isDefault) {
3036                 this.hasDefaultHeaders = true;
3037             }
3038             else {
3039                 this.hasHeaders = true;
3040             }
3041         },
3042
3043
3044         setHeader:function(o)
3045         {
3046             if (this.hasDefaultHeaders) {
3047                 for (var prop in this.defaultHeaders) {
3048                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3049                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3050                     }
3051                 }
3052             }
3053
3054             if (this.hasHeaders) {
3055                 for (var prop in this.headers) {
3056                     if (this.headers.hasOwnProperty(prop)) {
3057                         o.conn.setRequestHeader(prop, this.headers[prop]);
3058                     }
3059                 }
3060                 this.headers = {};
3061                 this.hasHeaders = false;
3062             }
3063         },
3064
3065         resetDefaultHeaders:function() {
3066             delete this.defaultHeaders;
3067             this.defaultHeaders = {};
3068             this.hasDefaultHeaders = false;
3069         },
3070
3071         abort:function(o, callback, isTimeout)
3072         {
3073             if(this.isCallInProgress(o)) {
3074                 o.conn.abort();
3075                 window.clearInterval(this.poll[o.tId]);
3076                 delete this.poll[o.tId];
3077                 if (isTimeout) {
3078                     delete this.timeout[o.tId];
3079                 }
3080
3081                 this.handleTransactionResponse(o, callback, true);
3082
3083                 return true;
3084             }
3085             else {
3086                 return false;
3087             }
3088         },
3089
3090
3091         isCallInProgress:function(o)
3092         {
3093             if (o && o.conn) {
3094                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3095             }
3096             else {
3097
3098                 return false;
3099             }
3100         },
3101
3102
3103         releaseObject:function(o)
3104         {
3105
3106             o.conn = null;
3107
3108             o = null;
3109         },
3110
3111         activeX:[
3112         'MSXML2.XMLHTTP.3.0',
3113         'MSXML2.XMLHTTP',
3114         'Microsoft.XMLHTTP'
3115         ]
3116
3117
3118     };
3119 })();/*
3120  * Portions of this file are based on pieces of Yahoo User Interface Library
3121  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3122  * YUI licensed under the BSD License:
3123  * http://developer.yahoo.net/yui/license.txt
3124  * <script type="text/javascript">
3125  *
3126  */
3127
3128 Roo.lib.Region = function(t, r, b, l) {
3129     this.top = t;
3130     this[1] = t;
3131     this.right = r;
3132     this.bottom = b;
3133     this.left = l;
3134     this[0] = l;
3135 };
3136
3137
3138 Roo.lib.Region.prototype = {
3139     contains : function(region) {
3140         return ( region.left >= this.left &&
3141                  region.right <= this.right &&
3142                  region.top >= this.top &&
3143                  region.bottom <= this.bottom    );
3144
3145     },
3146
3147     getArea : function() {
3148         return ( (this.bottom - this.top) * (this.right - this.left) );
3149     },
3150
3151     intersect : function(region) {
3152         var t = Math.max(this.top, region.top);
3153         var r = Math.min(this.right, region.right);
3154         var b = Math.min(this.bottom, region.bottom);
3155         var l = Math.max(this.left, region.left);
3156
3157         if (b >= t && r >= l) {
3158             return new Roo.lib.Region(t, r, b, l);
3159         } else {
3160             return null;
3161         }
3162     },
3163     union : function(region) {
3164         var t = Math.min(this.top, region.top);
3165         var r = Math.max(this.right, region.right);
3166         var b = Math.max(this.bottom, region.bottom);
3167         var l = Math.min(this.left, region.left);
3168
3169         return new Roo.lib.Region(t, r, b, l);
3170     },
3171
3172     adjust : function(t, l, b, r) {
3173         this.top += t;
3174         this.left += l;
3175         this.right += r;
3176         this.bottom += b;
3177         return this;
3178     }
3179 };
3180
3181 Roo.lib.Region.getRegion = function(el) {
3182     var p = Roo.lib.Dom.getXY(el);
3183
3184     var t = p[1];
3185     var r = p[0] + el.offsetWidth;
3186     var b = p[1] + el.offsetHeight;
3187     var l = p[0];
3188
3189     return new Roo.lib.Region(t, r, b, l);
3190 };
3191 /*
3192  * Portions of this file are based on pieces of Yahoo User Interface Library
3193  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3194  * YUI licensed under the BSD License:
3195  * http://developer.yahoo.net/yui/license.txt
3196  * <script type="text/javascript">
3197  *
3198  */
3199 //@@dep Roo.lib.Region
3200
3201
3202 Roo.lib.Point = function(x, y) {
3203     if (x instanceof Array) {
3204         y = x[1];
3205         x = x[0];
3206     }
3207     this.x = this.right = this.left = this[0] = x;
3208     this.y = this.top = this.bottom = this[1] = y;
3209 };
3210
3211 Roo.lib.Point.prototype = new Roo.lib.Region();
3212 /*
3213  * Portions of this file are based on pieces of Yahoo User Interface Library
3214  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3215  * YUI licensed under the BSD License:
3216  * http://developer.yahoo.net/yui/license.txt
3217  * <script type="text/javascript">
3218  *
3219  */
3220  
3221 (function() {   
3222
3223     Roo.lib.Anim = {
3224         scroll : function(el, args, duration, easing, cb, scope) {
3225             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3226         },
3227
3228         motion : function(el, args, duration, easing, cb, scope) {
3229             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3230         },
3231
3232         color : function(el, args, duration, easing, cb, scope) {
3233             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3234         },
3235
3236         run : function(el, args, duration, easing, cb, scope, type) {
3237             type = type || Roo.lib.AnimBase;
3238             if (typeof easing == "string") {
3239                 easing = Roo.lib.Easing[easing];
3240             }
3241             var anim = new type(el, args, duration, easing);
3242             anim.animateX(function() {
3243                 Roo.callback(cb, scope);
3244             });
3245             return anim;
3246         }
3247     };
3248 })();/*
3249  * Portions of this file are based on pieces of Yahoo User Interface Library
3250  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3251  * YUI licensed under the BSD License:
3252  * http://developer.yahoo.net/yui/license.txt
3253  * <script type="text/javascript">
3254  *
3255  */
3256
3257 (function() {    
3258     var libFlyweight;
3259     
3260     function fly(el) {
3261         if (!libFlyweight) {
3262             libFlyweight = new Roo.Element.Flyweight();
3263         }
3264         libFlyweight.dom = el;
3265         return libFlyweight;
3266     }
3267
3268     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3269     
3270    
3271     
3272     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3273         if (el) {
3274             this.init(el, attributes, duration, method);
3275         }
3276     };
3277
3278     Roo.lib.AnimBase.fly = fly;
3279     
3280     
3281     
3282     Roo.lib.AnimBase.prototype = {
3283
3284         toString: function() {
3285             var el = this.getEl();
3286             var id = el.id || el.tagName;
3287             return ("Anim " + id);
3288         },
3289
3290         patterns: {
3291             noNegatives:        /width|height|opacity|padding/i,
3292             offsetAttribute:  /^((width|height)|(top|left))$/,
3293             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3294             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3295         },
3296
3297
3298         doMethod: function(attr, start, end) {
3299             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3300         },
3301
3302
3303         setAttribute: function(attr, val, unit) {
3304             if (this.patterns.noNegatives.test(attr)) {
3305                 val = (val > 0) ? val : 0;
3306             }
3307
3308             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3309         },
3310
3311
3312         getAttribute: function(attr) {
3313             var el = this.getEl();
3314             var val = fly(el).getStyle(attr);
3315
3316             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3317                 return parseFloat(val);
3318             }
3319
3320             var a = this.patterns.offsetAttribute.exec(attr) || [];
3321             var pos = !!( a[3] );
3322             var box = !!( a[2] );
3323
3324
3325             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3326                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3327             } else {
3328                 val = 0;
3329             }
3330
3331             return val;
3332         },
3333
3334
3335         getDefaultUnit: function(attr) {
3336             if (this.patterns.defaultUnit.test(attr)) {
3337                 return 'px';
3338             }
3339
3340             return '';
3341         },
3342
3343         animateX : function(callback, scope) {
3344             var f = function() {
3345                 this.onComplete.removeListener(f);
3346                 if (typeof callback == "function") {
3347                     callback.call(scope || this, this);
3348                 }
3349             };
3350             this.onComplete.addListener(f, this);
3351             this.animate();
3352         },
3353
3354
3355         setRuntimeAttribute: function(attr) {
3356             var start;
3357             var end;
3358             var attributes = this.attributes;
3359
3360             this.runtimeAttributes[attr] = {};
3361
3362             var isset = function(prop) {
3363                 return (typeof prop !== 'undefined');
3364             };
3365
3366             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3367                 return false;
3368             }
3369
3370             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3371
3372
3373             if (isset(attributes[attr]['to'])) {
3374                 end = attributes[attr]['to'];
3375             } else if (isset(attributes[attr]['by'])) {
3376                 if (start.constructor == Array) {
3377                     end = [];
3378                     for (var i = 0, len = start.length; i < len; ++i) {
3379                         end[i] = start[i] + attributes[attr]['by'][i];
3380                     }
3381                 } else {
3382                     end = start + attributes[attr]['by'];
3383                 }
3384             }
3385
3386             this.runtimeAttributes[attr].start = start;
3387             this.runtimeAttributes[attr].end = end;
3388
3389
3390             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3391         },
3392
3393
3394         init: function(el, attributes, duration, method) {
3395
3396             var isAnimated = false;
3397
3398
3399             var startTime = null;
3400
3401
3402             var actualFrames = 0;
3403
3404
3405             el = Roo.getDom(el);
3406
3407
3408             this.attributes = attributes || {};
3409
3410
3411             this.duration = duration || 1;
3412
3413
3414             this.method = method || Roo.lib.Easing.easeNone;
3415
3416
3417             this.useSeconds = true;
3418
3419
3420             this.currentFrame = 0;
3421
3422
3423             this.totalFrames = Roo.lib.AnimMgr.fps;
3424
3425
3426             this.getEl = function() {
3427                 return el;
3428             };
3429
3430
3431             this.isAnimated = function() {
3432                 return isAnimated;
3433             };
3434
3435
3436             this.getStartTime = function() {
3437                 return startTime;
3438             };
3439
3440             this.runtimeAttributes = {};
3441
3442
3443             this.animate = function() {
3444                 if (this.isAnimated()) {
3445                     return false;
3446                 }
3447
3448                 this.currentFrame = 0;
3449
3450                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3451
3452                 Roo.lib.AnimMgr.registerElement(this);
3453             };
3454
3455
3456             this.stop = function(finish) {
3457                 if (finish) {
3458                     this.currentFrame = this.totalFrames;
3459                     this._onTween.fire();
3460                 }
3461                 Roo.lib.AnimMgr.stop(this);
3462             };
3463
3464             var onStart = function() {
3465                 this.onStart.fire();
3466
3467                 this.runtimeAttributes = {};
3468                 for (var attr in this.attributes) {
3469                     this.setRuntimeAttribute(attr);
3470                 }
3471
3472                 isAnimated = true;
3473                 actualFrames = 0;
3474                 startTime = new Date();
3475             };
3476
3477
3478             var onTween = function() {
3479                 var data = {
3480                     duration: new Date() - this.getStartTime(),
3481                     currentFrame: this.currentFrame
3482                 };
3483
3484                 data.toString = function() {
3485                     return (
3486                             'duration: ' + data.duration +
3487                             ', currentFrame: ' + data.currentFrame
3488                             );
3489                 };
3490
3491                 this.onTween.fire(data);
3492
3493                 var runtimeAttributes = this.runtimeAttributes;
3494
3495                 for (var attr in runtimeAttributes) {
3496                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3497                 }
3498
3499                 actualFrames += 1;
3500             };
3501
3502             var onComplete = function() {
3503                 var actual_duration = (new Date() - startTime) / 1000 ;
3504
3505                 var data = {
3506                     duration: actual_duration,
3507                     frames: actualFrames,
3508                     fps: actualFrames / actual_duration
3509                 };
3510
3511                 data.toString = function() {
3512                     return (
3513                             'duration: ' + data.duration +
3514                             ', frames: ' + data.frames +
3515                             ', fps: ' + data.fps
3516                             );
3517                 };
3518
3519                 isAnimated = false;
3520                 actualFrames = 0;
3521                 this.onComplete.fire(data);
3522             };
3523
3524
3525             this._onStart = new Roo.util.Event(this);
3526             this.onStart = new Roo.util.Event(this);
3527             this.onTween = new Roo.util.Event(this);
3528             this._onTween = new Roo.util.Event(this);
3529             this.onComplete = new Roo.util.Event(this);
3530             this._onComplete = new Roo.util.Event(this);
3531             this._onStart.addListener(onStart);
3532             this._onTween.addListener(onTween);
3533             this._onComplete.addListener(onComplete);
3534         }
3535     };
3536 })();
3537 /*
3538  * Portions of this file are based on pieces of Yahoo User Interface Library
3539  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3540  * YUI licensed under the BSD License:
3541  * http://developer.yahoo.net/yui/license.txt
3542  * <script type="text/javascript">
3543  *
3544  */
3545
3546 Roo.lib.AnimMgr = new function() {
3547
3548     var thread = null;
3549
3550
3551     var queue = [];
3552
3553
3554     var tweenCount = 0;
3555
3556
3557     this.fps = 1000;
3558
3559
3560     this.delay = 1;
3561
3562
3563     this.registerElement = function(tween) {
3564         queue[queue.length] = tween;
3565         tweenCount += 1;
3566         tween._onStart.fire();
3567         this.start();
3568     };
3569
3570
3571     this.unRegister = function(tween, index) {
3572         tween._onComplete.fire();
3573         index = index || getIndex(tween);
3574         if (index != -1) {
3575             queue.splice(index, 1);
3576         }
3577
3578         tweenCount -= 1;
3579         if (tweenCount <= 0) {
3580             this.stop();
3581         }
3582     };
3583
3584
3585     this.start = function() {
3586         if (thread === null) {
3587             thread = setInterval(this.run, this.delay);
3588         }
3589     };
3590
3591
3592     this.stop = function(tween) {
3593         if (!tween) {
3594             clearInterval(thread);
3595
3596             for (var i = 0, len = queue.length; i < len; ++i) {
3597                 if (queue[0].isAnimated()) {
3598                     this.unRegister(queue[0], 0);
3599                 }
3600             }
3601
3602             queue = [];
3603             thread = null;
3604             tweenCount = 0;
3605         }
3606         else {
3607             this.unRegister(tween);
3608         }
3609     };
3610
3611
3612     this.run = function() {
3613         for (var i = 0, len = queue.length; i < len; ++i) {
3614             var tween = queue[i];
3615             if (!tween || !tween.isAnimated()) {
3616                 continue;
3617             }
3618
3619             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3620             {
3621                 tween.currentFrame += 1;
3622
3623                 if (tween.useSeconds) {
3624                     correctFrame(tween);
3625                 }
3626                 tween._onTween.fire();
3627             }
3628             else {
3629                 Roo.lib.AnimMgr.stop(tween, i);
3630             }
3631         }
3632     };
3633
3634     var getIndex = function(anim) {
3635         for (var i = 0, len = queue.length; i < len; ++i) {
3636             if (queue[i] == anim) {
3637                 return i;
3638             }
3639         }
3640         return -1;
3641     };
3642
3643
3644     var correctFrame = function(tween) {
3645         var frames = tween.totalFrames;
3646         var frame = tween.currentFrame;
3647         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3648         var elapsed = (new Date() - tween.getStartTime());
3649         var tweak = 0;
3650
3651         if (elapsed < tween.duration * 1000) {
3652             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3653         } else {
3654             tweak = frames - (frame + 1);
3655         }
3656         if (tweak > 0 && isFinite(tweak)) {
3657             if (tween.currentFrame + tweak >= frames) {
3658                 tweak = frames - (frame + 1);
3659             }
3660
3661             tween.currentFrame += tweak;
3662         }
3663     };
3664 };
3665
3666     /*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 Roo.lib.Bezier = new function() {
3675
3676         this.getPosition = function(points, t) {
3677             var n = points.length;
3678             var tmp = [];
3679
3680             for (var i = 0; i < n; ++i) {
3681                 tmp[i] = [points[i][0], points[i][1]];
3682             }
3683
3684             for (var j = 1; j < n; ++j) {
3685                 for (i = 0; i < n - j; ++i) {
3686                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3687                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3688                 }
3689             }
3690
3691             return [ tmp[0][0], tmp[0][1] ];
3692
3693         };
3694     }; 
3695
3696 /**
3697  * @class Roo.lib.Color
3698  * @constructor
3699  * An abstract Color implementation. Concrete Color implementations should use
3700  * an instance of this function as their prototype, and implement the getRGB and
3701  * getHSL functions. getRGB should return an object representing the RGB
3702  * components of this Color, with the red, green, and blue components in the
3703  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3704  * return an object representing the HSL components of this Color, with the hue
3705  * component in the range [0,360), the saturation and lightness components in
3706  * the range [0,100], and the alpha component in the range [0,1].
3707  *
3708  *
3709  * Color.js
3710  *
3711  * Functions for Color handling and processing.
3712  *
3713  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3714  *
3715  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3716  * rights to this program, with the intention of it becoming part of the public
3717  * domain. Because this program is released into the public domain, it comes with
3718  * no warranty either expressed or implied, to the extent permitted by law.
3719  * 
3720  * For more free and public domain JavaScript code by the same author, visit:
3721  * http://www.safalra.com/web-design/javascript/
3722  * 
3723  */
3724 Roo.lib.Color = function() { }
3725
3726
3727 Roo.apply(Roo.lib.Color.prototype, {
3728   
3729   rgb : null,
3730   hsv : null,
3731   hsl : null,
3732   
3733   /**
3734    * getIntegerRGB
3735    * @return {Object} an object representing the RGBA components of this Color. The red,
3736    * green, and blue components are converted to integers in the range [0,255].
3737    * The alpha is a value in the range [0,1].
3738    */
3739   getIntegerRGB : function(){
3740
3741     // get the RGB components of this Color
3742     var rgb = this.getRGB();
3743
3744     // return the integer components
3745     return {
3746       'r' : Math.round(rgb.r),
3747       'g' : Math.round(rgb.g),
3748       'b' : Math.round(rgb.b),
3749       'a' : rgb.a
3750     };
3751
3752   },
3753
3754   /**
3755    * getPercentageRGB
3756    * @return {Object} an object representing the RGBA components of this Color. The red,
3757    * green, and blue components are converted to numbers in the range [0,100].
3758    * The alpha is a value in the range [0,1].
3759    */
3760   getPercentageRGB : function(){
3761
3762     // get the RGB components of this Color
3763     var rgb = this.getRGB();
3764
3765     // return the percentage components
3766     return {
3767       'r' : 100 * rgb.r / 255,
3768       'g' : 100 * rgb.g / 255,
3769       'b' : 100 * rgb.b / 255,
3770       'a' : rgb.a
3771     };
3772
3773   },
3774
3775   /**
3776    * getCSSHexadecimalRGB
3777    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3778    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3779    * are two-digit hexadecimal numbers.
3780    */
3781   getCSSHexadecimalRGB : function()
3782   {
3783
3784     // get the integer RGB components
3785     var rgb = this.getIntegerRGB();
3786
3787     // determine the hexadecimal equivalents
3788     var r16 = rgb.r.toString(16);
3789     var g16 = rgb.g.toString(16);
3790     var b16 = rgb.b.toString(16);
3791
3792     // return the CSS RGB Color value
3793     return '#'
3794         + (r16.length == 2 ? r16 : '0' + r16)
3795         + (g16.length == 2 ? g16 : '0' + g16)
3796         + (b16.length == 2 ? b16 : '0' + b16);
3797
3798   },
3799
3800   /**
3801    * getCSSIntegerRGB
3802    * @return {String} a string representing this Color as a CSS integer RGB Color
3803    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3804    * are integers in the range [0,255].
3805    */
3806   getCSSIntegerRGB : function(){
3807
3808     // get the integer RGB components
3809     var rgb = this.getIntegerRGB();
3810
3811     // return the CSS RGB Color value
3812     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3813
3814   },
3815
3816   /**
3817    * getCSSIntegerRGBA
3818    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3819    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3820    * b are integers in the range [0,255] and a is in the range [0,1].
3821    */
3822   getCSSIntegerRGBA : function(){
3823
3824     // get the integer RGB components
3825     var rgb = this.getIntegerRGB();
3826
3827     // return the CSS integer RGBA Color value
3828     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3829
3830   },
3831
3832   /**
3833    * getCSSPercentageRGB
3834    * @return {String} a string representing this Color as a CSS percentage RGB Color
3835    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3836    * b are in the range [0,100].
3837    */
3838   getCSSPercentageRGB : function(){
3839
3840     // get the percentage RGB components
3841     var rgb = this.getPercentageRGB();
3842
3843     // return the CSS RGB Color value
3844     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3845
3846   },
3847
3848   /**
3849    * getCSSPercentageRGBA
3850    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3851    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3852    * and b are in the range [0,100] and a is in the range [0,1].
3853    */
3854   getCSSPercentageRGBA : function(){
3855
3856     // get the percentage RGB components
3857     var rgb = this.getPercentageRGB();
3858
3859     // return the CSS percentage RGBA Color value
3860     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3861
3862   },
3863
3864   /**
3865    * getCSSHSL
3866    * @return {String} a string representing this Color as a CSS HSL Color value - that
3867    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3868    * s and l are in the range [0,100].
3869    */
3870   getCSSHSL : function(){
3871
3872     // get the HSL components
3873     var hsl = this.getHSL();
3874
3875     // return the CSS HSL Color value
3876     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3877
3878   },
3879
3880   /**
3881    * getCSSHSLA
3882    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3883    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3884    * s and l are in the range [0,100], and a is in the range [0,1].
3885    */
3886   getCSSHSLA : function(){
3887
3888     // get the HSL components
3889     var hsl = this.getHSL();
3890
3891     // return the CSS HSL Color value
3892     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3893
3894   },
3895
3896   /**
3897    * Sets the Color of the specified node to this Color. This functions sets
3898    * the CSS 'color' property for the node. The parameter is:
3899    * 
3900    * @param {DomElement} node - the node whose Color should be set
3901    */
3902   setNodeColor : function(node){
3903
3904     // set the Color of the node
3905     node.style.color = this.getCSSHexadecimalRGB();
3906
3907   },
3908
3909   /**
3910    * Sets the background Color of the specified node to this Color. This
3911    * functions sets the CSS 'background-color' property for the node. The
3912    * parameter is:
3913    *
3914    * @param {DomElement} node - the node whose background Color should be set
3915    */
3916   setNodeBackgroundColor : function(node){
3917
3918     // set the background Color of the node
3919     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3920
3921   },
3922   // convert between formats..
3923   toRGB: function()
3924   {
3925     var r = this.getIntegerRGB();
3926     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3927     
3928   },
3929   toHSL : function()
3930   {
3931      var hsl = this.getHSL();
3932   // return the CSS HSL Color value
3933     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3934     
3935   },
3936   
3937   toHSV : function()
3938   {
3939     var rgb = this.toRGB();
3940     var hsv = rgb.getHSV();
3941    // return the CSS HSL Color value
3942     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3943     
3944   },
3945   
3946   // modify  v = 0 ... 1 (eg. 0.5)
3947   saturate : function(v)
3948   {
3949       var rgb = this.toRGB();
3950       var hsv = rgb.getHSV();
3951       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3952       
3953   
3954   },
3955   
3956    
3957   /**
3958    * getRGB
3959    * @return {Object} the RGB and alpha components of this Color as an object with r,
3960    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3961    * the range [0,1].
3962    */
3963   getRGB: function(){
3964    
3965     // return the RGB components
3966     return {
3967       'r' : this.rgb.r,
3968       'g' : this.rgb.g,
3969       'b' : this.rgb.b,
3970       'a' : this.alpha
3971     };
3972
3973   },
3974
3975   /**
3976    * getHSV
3977    * @return {Object} the HSV and alpha components of this Color as an object with h,
3978    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3979    * [0,100], and a is in the range [0,1].
3980    */
3981   getHSV : function()
3982   {
3983     
3984     // calculate the HSV components if necessary
3985     if (this.hsv == null) {
3986       this.calculateHSV();
3987     }
3988
3989     // return the HSV components
3990     return {
3991       'h' : this.hsv.h,
3992       's' : this.hsv.s,
3993       'v' : this.hsv.v,
3994       'a' : this.alpha
3995     };
3996
3997   },
3998
3999   /**
4000    * getHSL
4001    * @return {Object} the HSL and alpha components of this Color as an object with h,
4002    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4003    * [0,100], and a is in the range [0,1].
4004    */
4005   getHSL : function(){
4006     
4007      
4008     // calculate the HSV components if necessary
4009     if (this.hsl == null) { this.calculateHSL(); }
4010
4011     // return the HSL components
4012     return {
4013       'h' : this.hsl.h,
4014       's' : this.hsl.s,
4015       'l' : this.hsl.l,
4016       'a' : this.alpha
4017     };
4018
4019   }
4020   
4021
4022 });
4023
4024
4025 /**
4026  * @class Roo.lib.RGBColor
4027  * @extends Roo.lib.Color
4028  * Creates a Color specified in the RGB Color space, with an optional alpha
4029  * component. The parameters are:
4030  * @constructor
4031  * 
4032
4033  * @param {Number} r - the red component, clipped to the range [0,255]
4034  * @param {Number} g - the green component, clipped to the range [0,255]
4035  * @param {Number} b - the blue component, clipped to the range [0,255]
4036  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4037  *     optional and defaults to 1
4038  */
4039 Roo.lib.RGBColor = function (r, g, b, a){
4040
4041   // store the alpha component after clipping it if necessary
4042   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4043
4044   // store the RGB components after clipping them if necessary
4045   this.rgb =
4046       {
4047         'r' : Math.max(0, Math.min(255, r)),
4048         'g' : Math.max(0, Math.min(255, g)),
4049         'b' : Math.max(0, Math.min(255, b))
4050       };
4051
4052   // initialise the HSV and HSL components to null
4053   
4054
4055   /* 
4056    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4057    * range [0,360). The parameters are:
4058    *
4059    * maximum - the maximum of the RGB component values
4060    * range   - the range of the RGB component values
4061    */
4062    
4063
4064 }
4065 // this does an 'exteds'
4066 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4067
4068   
4069     getHue  : function(maximum, range)
4070     {
4071       var rgb = this.rgb;
4072        
4073       // check whether the range is zero
4074       if (range == 0){
4075   
4076         // set the hue to zero (any hue is acceptable as the Color is grey)
4077         var hue = 0;
4078   
4079       }else{
4080   
4081         // determine which of the components has the highest value and set the hue
4082         switch (maximum){
4083   
4084           // red has the highest value
4085           case rgb.r:
4086             var hue = (rgb.g - rgb.b) / range * 60;
4087             if (hue < 0) { hue += 360; }
4088             break;
4089   
4090           // green has the highest value
4091           case rgb.g:
4092             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4093             break;
4094   
4095           // blue has the highest value
4096           case rgb.b:
4097             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4098             break;
4099   
4100         }
4101   
4102       }
4103   
4104       // return the hue
4105       return hue;
4106   
4107     },
4108
4109   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4110    * be returned be the getHSV function.
4111    */
4112    calculateHSV : function(){
4113     var rgb = this.rgb;
4114     // get the maximum and range of the RGB component values
4115     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4116     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4117
4118     // store the HSV components
4119     this.hsv =
4120         {
4121           'h' : this.getHue(maximum, range),
4122           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4123           'v' : maximum / 2.55
4124         };
4125
4126   },
4127
4128   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4129    * be returned be the getHSL function.
4130    */
4131    calculateHSL : function(){
4132     var rgb = this.rgb;
4133     // get the maximum and range of the RGB component values
4134     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4135     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4136
4137     // determine the lightness in the range [0,1]
4138     var l = maximum / 255 - range / 510;
4139
4140     // store the HSL components
4141     this.hsl =
4142         {
4143           'h' : this.getHue(maximum, range),
4144           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4145           'l' : 100 * l
4146         };
4147
4148   }
4149
4150 });
4151
4152 /**
4153  * @class Roo.lib.HSVColor
4154  * @extends Roo.lib.Color
4155  * Creates a Color specified in the HSV Color space, with an optional alpha
4156  * component. The parameters are:
4157  * @constructor
4158  *
4159  * @param {Number} h - the hue component, wrapped to the range [0,360)
4160  * @param {Number} s - the saturation component, clipped to the range [0,100]
4161  * @param {Number} v - the value component, clipped to the range [0,100]
4162  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4163  *     optional and defaults to 1
4164  */
4165 Roo.lib.HSVColor = function (h, s, v, a){
4166
4167   // store the alpha component after clipping it if necessary
4168   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4169
4170   // store the HSV components after clipping or wrapping them if necessary
4171   this.hsv =
4172       {
4173         'h' : (h % 360 + 360) % 360,
4174         's' : Math.max(0, Math.min(100, s)),
4175         'v' : Math.max(0, Math.min(100, v))
4176       };
4177
4178   // initialise the RGB and HSL components to null
4179   this.rgb = null;
4180   this.hsl = null;
4181 }
4182
4183 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4184   /* Calculates and stores the RGB components of this HSVColor so that they can
4185    * be returned be the getRGB function.
4186    */
4187   calculateRGB: function ()
4188   {
4189     var hsv = this.hsv;
4190     // check whether the saturation is zero
4191     if (hsv.s == 0){
4192
4193       // set the Color to the appropriate shade of grey
4194       var r = hsv.v;
4195       var g = hsv.v;
4196       var b = hsv.v;
4197
4198     }else{
4199
4200       // set some temporary values
4201       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4202       var p  = hsv.v * (1 - hsv.s / 100);
4203       var q  = hsv.v * (1 - hsv.s / 100 * f);
4204       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4205
4206       // set the RGB Color components to their temporary values
4207       switch (Math.floor(hsv.h / 60)){
4208         case 0: var r = hsv.v; var g = t; var b = p; break;
4209         case 1: var r = q; var g = hsv.v; var b = p; break;
4210         case 2: var r = p; var g = hsv.v; var b = t; break;
4211         case 3: var r = p; var g = q; var b = hsv.v; break;
4212         case 4: var r = t; var g = p; var b = hsv.v; break;
4213         case 5: var r = hsv.v; var g = p; var b = q; break;
4214       }
4215
4216     }
4217
4218     // store the RGB components
4219     this.rgb =
4220         {
4221           'r' : r * 2.55,
4222           'g' : g * 2.55,
4223           'b' : b * 2.55
4224         };
4225
4226   },
4227
4228   /* Calculates and stores the HSL components of this HSVColor so that they can
4229    * be returned be the getHSL function.
4230    */
4231   calculateHSL : function (){
4232
4233     var hsv = this.hsv;
4234     // determine the lightness in the range [0,100]
4235     var l = (2 - hsv.s / 100) * hsv.v / 2;
4236
4237     // store the HSL components
4238     this.hsl =
4239         {
4240           'h' : hsv.h,
4241           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4242           'l' : l
4243         };
4244
4245     // correct a division-by-zero error
4246     if (isNaN(hsl.s)) { hsl.s = 0; }
4247
4248   } 
4249  
4250
4251 });
4252  
4253
4254 /**
4255  * @class Roo.lib.HSLColor
4256  * @extends Roo.lib.Color
4257  *
4258  * @constructor
4259  * Creates a Color specified in the HSL Color space, with an optional alpha
4260  * component. The parameters are:
4261  *
4262  * @param {Number} h - the hue component, wrapped to the range [0,360)
4263  * @param {Number} s - the saturation component, clipped to the range [0,100]
4264  * @param {Number} l - the lightness component, clipped to the range [0,100]
4265  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4266  *     optional and defaults to 1
4267  */
4268
4269 Roo.lib.HSLColor = function(h, s, l, a){
4270
4271   // store the alpha component after clipping it if necessary
4272   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4273
4274   // store the HSL components after clipping or wrapping them if necessary
4275   this.hsl =
4276       {
4277         'h' : (h % 360 + 360) % 360,
4278         's' : Math.max(0, Math.min(100, s)),
4279         'l' : Math.max(0, Math.min(100, l))
4280       };
4281
4282   // initialise the RGB and HSV components to null
4283 }
4284
4285 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4286
4287   /* Calculates and stores the RGB components of this HSLColor so that they can
4288    * be returned be the getRGB function.
4289    */
4290   calculateRGB: function (){
4291
4292     // check whether the saturation is zero
4293     if (this.hsl.s == 0){
4294
4295       // store the RGB components representing the appropriate shade of grey
4296       this.rgb =
4297           {
4298             'r' : this.hsl.l * 2.55,
4299             'g' : this.hsl.l * 2.55,
4300             'b' : this.hsl.l * 2.55
4301           };
4302
4303     }else{
4304
4305       // set some temporary values
4306       var p = this.hsl.l < 50
4307             ? this.hsl.l * (1 + hsl.s / 100)
4308             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4309       var q = 2 * hsl.l - p;
4310
4311       // initialise the RGB components
4312       this.rgb =
4313           {
4314             'r' : (h + 120) / 60 % 6,
4315             'g' : h / 60,
4316             'b' : (h + 240) / 60 % 6
4317           };
4318
4319       // loop over the RGB components
4320       for (var key in this.rgb){
4321
4322         // ensure that the property is not inherited from the root object
4323         if (this.rgb.hasOwnProperty(key)){
4324
4325           // set the component to its value in the range [0,100]
4326           if (this.rgb[key] < 1){
4327             this.rgb[key] = q + (p - q) * this.rgb[key];
4328           }else if (this.rgb[key] < 3){
4329             this.rgb[key] = p;
4330           }else if (this.rgb[key] < 4){
4331             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4332           }else{
4333             this.rgb[key] = q;
4334           }
4335
4336           // set the component to its value in the range [0,255]
4337           this.rgb[key] *= 2.55;
4338
4339         }
4340
4341       }
4342
4343     }
4344
4345   },
4346
4347   /* Calculates and stores the HSV components of this HSLColor so that they can
4348    * be returned be the getHSL function.
4349    */
4350    calculateHSV : function(){
4351
4352     // set a temporary value
4353     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4354
4355     // store the HSV components
4356     this.hsv =
4357         {
4358           'h' : this.hsl.h,
4359           's' : 200 * t / (this.hsl.l + t),
4360           'v' : t + this.hsl.l
4361         };
4362
4363     // correct a division-by-zero error
4364     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4365
4366   }
4367  
4368
4369 });
4370 /*
4371  * Portions of this file are based on pieces of Yahoo User Interface Library
4372  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4373  * YUI licensed under the BSD License:
4374  * http://developer.yahoo.net/yui/license.txt
4375  * <script type="text/javascript">
4376  *
4377  */
4378 (function() {
4379
4380     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4381         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4382     };
4383
4384     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4385
4386     var fly = Roo.lib.AnimBase.fly;
4387     var Y = Roo.lib;
4388     var superclass = Y.ColorAnim.superclass;
4389     var proto = Y.ColorAnim.prototype;
4390
4391     proto.toString = function() {
4392         var el = this.getEl();
4393         var id = el.id || el.tagName;
4394         return ("ColorAnim " + id);
4395     };
4396
4397     proto.patterns.color = /color$/i;
4398     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4399     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4400     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4401     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4402
4403
4404     proto.parseColor = function(s) {
4405         if (s.length == 3) {
4406             return s;
4407         }
4408
4409         var c = this.patterns.hex.exec(s);
4410         if (c && c.length == 4) {
4411             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4412         }
4413
4414         c = this.patterns.rgb.exec(s);
4415         if (c && c.length == 4) {
4416             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4417         }
4418
4419         c = this.patterns.hex3.exec(s);
4420         if (c && c.length == 4) {
4421             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4422         }
4423
4424         return null;
4425     };
4426     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4427     proto.getAttribute = function(attr) {
4428         var el = this.getEl();
4429         if (this.patterns.color.test(attr)) {
4430             var val = fly(el).getStyle(attr);
4431
4432             if (this.patterns.transparent.test(val)) {
4433                 var parent = el.parentNode;
4434                 val = fly(parent).getStyle(attr);
4435
4436                 while (parent && this.patterns.transparent.test(val)) {
4437                     parent = parent.parentNode;
4438                     val = fly(parent).getStyle(attr);
4439                     if (parent.tagName.toUpperCase() == 'HTML') {
4440                         val = '#fff';
4441                     }
4442                 }
4443             }
4444         } else {
4445             val = superclass.getAttribute.call(this, attr);
4446         }
4447
4448         return val;
4449     };
4450     proto.getAttribute = function(attr) {
4451         var el = this.getEl();
4452         if (this.patterns.color.test(attr)) {
4453             var val = fly(el).getStyle(attr);
4454
4455             if (this.patterns.transparent.test(val)) {
4456                 var parent = el.parentNode;
4457                 val = fly(parent).getStyle(attr);
4458
4459                 while (parent && this.patterns.transparent.test(val)) {
4460                     parent = parent.parentNode;
4461                     val = fly(parent).getStyle(attr);
4462                     if (parent.tagName.toUpperCase() == 'HTML') {
4463                         val = '#fff';
4464                     }
4465                 }
4466             }
4467         } else {
4468             val = superclass.getAttribute.call(this, attr);
4469         }
4470
4471         return val;
4472     };
4473
4474     proto.doMethod = function(attr, start, end) {
4475         var val;
4476
4477         if (this.patterns.color.test(attr)) {
4478             val = [];
4479             for (var i = 0, len = start.length; i < len; ++i) {
4480                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4481             }
4482
4483             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4484         }
4485         else {
4486             val = superclass.doMethod.call(this, attr, start, end);
4487         }
4488
4489         return val;
4490     };
4491
4492     proto.setRuntimeAttribute = function(attr) {
4493         superclass.setRuntimeAttribute.call(this, attr);
4494
4495         if (this.patterns.color.test(attr)) {
4496             var attributes = this.attributes;
4497             var start = this.parseColor(this.runtimeAttributes[attr].start);
4498             var end = this.parseColor(this.runtimeAttributes[attr].end);
4499
4500             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4501                 end = this.parseColor(attributes[attr].by);
4502
4503                 for (var i = 0, len = start.length; i < len; ++i) {
4504                     end[i] = start[i] + end[i];
4505                 }
4506             }
4507
4508             this.runtimeAttributes[attr].start = start;
4509             this.runtimeAttributes[attr].end = end;
4510         }
4511     };
4512 })();
4513
4514 /*
4515  * Portions of this file are based on pieces of Yahoo User Interface Library
4516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4517  * YUI licensed under the BSD License:
4518  * http://developer.yahoo.net/yui/license.txt
4519  * <script type="text/javascript">
4520  *
4521  */
4522 Roo.lib.Easing = {
4523
4524
4525     easeNone: function (t, b, c, d) {
4526         return c * t / d + b;
4527     },
4528
4529
4530     easeIn: function (t, b, c, d) {
4531         return c * (t /= d) * t + b;
4532     },
4533
4534
4535     easeOut: function (t, b, c, d) {
4536         return -c * (t /= d) * (t - 2) + b;
4537     },
4538
4539
4540     easeBoth: function (t, b, c, d) {
4541         if ((t /= d / 2) < 1) {
4542             return c / 2 * t * t + b;
4543         }
4544
4545         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4546     },
4547
4548
4549     easeInStrong: function (t, b, c, d) {
4550         return c * (t /= d) * t * t * t + b;
4551     },
4552
4553
4554     easeOutStrong: function (t, b, c, d) {
4555         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4556     },
4557
4558
4559     easeBothStrong: function (t, b, c, d) {
4560         if ((t /= d / 2) < 1) {
4561             return c / 2 * t * t * t * t + b;
4562         }
4563
4564         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4565     },
4566
4567
4568
4569     elasticIn: function (t, b, c, d, a, p) {
4570         if (t == 0) {
4571             return b;
4572         }
4573         if ((t /= d) == 1) {
4574             return b + c;
4575         }
4576         if (!p) {
4577             p = d * .3;
4578         }
4579
4580         if (!a || a < Math.abs(c)) {
4581             a = c;
4582             var s = p / 4;
4583         }
4584         else {
4585             var s = p / (2 * Math.PI) * Math.asin(c / a);
4586         }
4587
4588         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4589     },
4590
4591
4592     elasticOut: function (t, b, c, d, a, p) {
4593         if (t == 0) {
4594             return b;
4595         }
4596         if ((t /= d) == 1) {
4597             return b + c;
4598         }
4599         if (!p) {
4600             p = d * .3;
4601         }
4602
4603         if (!a || a < Math.abs(c)) {
4604             a = c;
4605             var s = p / 4;
4606         }
4607         else {
4608             var s = p / (2 * Math.PI) * Math.asin(c / a);
4609         }
4610
4611         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4612     },
4613
4614
4615     elasticBoth: function (t, b, c, d, a, p) {
4616         if (t == 0) {
4617             return b;
4618         }
4619
4620         if ((t /= d / 2) == 2) {
4621             return b + c;
4622         }
4623
4624         if (!p) {
4625             p = d * (.3 * 1.5);
4626         }
4627
4628         if (!a || a < Math.abs(c)) {
4629             a = c;
4630             var s = p / 4;
4631         }
4632         else {
4633             var s = p / (2 * Math.PI) * Math.asin(c / a);
4634         }
4635
4636         if (t < 1) {
4637             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4638                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4639         }
4640         return a * Math.pow(2, -10 * (t -= 1)) *
4641                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4642     },
4643
4644
4645
4646     backIn: function (t, b, c, d, s) {
4647         if (typeof s == 'undefined') {
4648             s = 1.70158;
4649         }
4650         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4651     },
4652
4653
4654     backOut: function (t, b, c, d, s) {
4655         if (typeof s == 'undefined') {
4656             s = 1.70158;
4657         }
4658         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4659     },
4660
4661
4662     backBoth: function (t, b, c, d, s) {
4663         if (typeof s == 'undefined') {
4664             s = 1.70158;
4665         }
4666
4667         if ((t /= d / 2 ) < 1) {
4668             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4669         }
4670         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4671     },
4672
4673
4674     bounceIn: function (t, b, c, d) {
4675         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4676     },
4677
4678
4679     bounceOut: function (t, b, c, d) {
4680         if ((t /= d) < (1 / 2.75)) {
4681             return c * (7.5625 * t * t) + b;
4682         } else if (t < (2 / 2.75)) {
4683             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4684         } else if (t < (2.5 / 2.75)) {
4685             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4686         }
4687         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4688     },
4689
4690
4691     bounceBoth: function (t, b, c, d) {
4692         if (t < d / 2) {
4693             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4694         }
4695         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4696     }
4697 };/*
4698  * Portions of this file are based on pieces of Yahoo User Interface Library
4699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4700  * YUI licensed under the BSD License:
4701  * http://developer.yahoo.net/yui/license.txt
4702  * <script type="text/javascript">
4703  *
4704  */
4705     (function() {
4706         Roo.lib.Motion = function(el, attributes, duration, method) {
4707             if (el) {
4708                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4709             }
4710         };
4711
4712         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4713
4714
4715         var Y = Roo.lib;
4716         var superclass = Y.Motion.superclass;
4717         var proto = Y.Motion.prototype;
4718
4719         proto.toString = function() {
4720             var el = this.getEl();
4721             var id = el.id || el.tagName;
4722             return ("Motion " + id);
4723         };
4724
4725         proto.patterns.points = /^points$/i;
4726
4727         proto.setAttribute = function(attr, val, unit) {
4728             if (this.patterns.points.test(attr)) {
4729                 unit = unit || 'px';
4730                 superclass.setAttribute.call(this, 'left', val[0], unit);
4731                 superclass.setAttribute.call(this, 'top', val[1], unit);
4732             } else {
4733                 superclass.setAttribute.call(this, attr, val, unit);
4734             }
4735         };
4736
4737         proto.getAttribute = function(attr) {
4738             if (this.patterns.points.test(attr)) {
4739                 var val = [
4740                         superclass.getAttribute.call(this, 'left'),
4741                         superclass.getAttribute.call(this, 'top')
4742                         ];
4743             } else {
4744                 val = superclass.getAttribute.call(this, attr);
4745             }
4746
4747             return val;
4748         };
4749
4750         proto.doMethod = function(attr, start, end) {
4751             var val = null;
4752
4753             if (this.patterns.points.test(attr)) {
4754                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4755                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4756             } else {
4757                 val = superclass.doMethod.call(this, attr, start, end);
4758             }
4759             return val;
4760         };
4761
4762         proto.setRuntimeAttribute = function(attr) {
4763             if (this.patterns.points.test(attr)) {
4764                 var el = this.getEl();
4765                 var attributes = this.attributes;
4766                 var start;
4767                 var control = attributes['points']['control'] || [];
4768                 var end;
4769                 var i, len;
4770
4771                 if (control.length > 0 && !(control[0] instanceof Array)) {
4772                     control = [control];
4773                 } else {
4774                     var tmp = [];
4775                     for (i = 0,len = control.length; i < len; ++i) {
4776                         tmp[i] = control[i];
4777                     }
4778                     control = tmp;
4779                 }
4780
4781                 Roo.fly(el).position();
4782
4783                 if (isset(attributes['points']['from'])) {
4784                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4785                 }
4786                 else {
4787                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4788                 }
4789
4790                 start = this.getAttribute('points');
4791
4792
4793                 if (isset(attributes['points']['to'])) {
4794                     end = translateValues.call(this, attributes['points']['to'], start);
4795
4796                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4797                     for (i = 0,len = control.length; i < len; ++i) {
4798                         control[i] = translateValues.call(this, control[i], start);
4799                     }
4800
4801
4802                 } else if (isset(attributes['points']['by'])) {
4803                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4804
4805                     for (i = 0,len = control.length; i < len; ++i) {
4806                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4807                     }
4808                 }
4809
4810                 this.runtimeAttributes[attr] = [start];
4811
4812                 if (control.length > 0) {
4813                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4814                 }
4815
4816                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4817             }
4818             else {
4819                 superclass.setRuntimeAttribute.call(this, attr);
4820             }
4821         };
4822
4823         var translateValues = function(val, start) {
4824             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4825             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4826
4827             return val;
4828         };
4829
4830         var isset = function(prop) {
4831             return (typeof prop !== 'undefined');
4832         };
4833     })();
4834 /*
4835  * Portions of this file are based on pieces of Yahoo User Interface Library
4836  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4837  * YUI licensed under the BSD License:
4838  * http://developer.yahoo.net/yui/license.txt
4839  * <script type="text/javascript">
4840  *
4841  */
4842     (function() {
4843         Roo.lib.Scroll = function(el, attributes, duration, method) {
4844             if (el) {
4845                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4846             }
4847         };
4848
4849         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4850
4851
4852         var Y = Roo.lib;
4853         var superclass = Y.Scroll.superclass;
4854         var proto = Y.Scroll.prototype;
4855
4856         proto.toString = function() {
4857             var el = this.getEl();
4858             var id = el.id || el.tagName;
4859             return ("Scroll " + id);
4860         };
4861
4862         proto.doMethod = function(attr, start, end) {
4863             var val = null;
4864
4865             if (attr == 'scroll') {
4866                 val = [
4867                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4868                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4869                         ];
4870
4871             } else {
4872                 val = superclass.doMethod.call(this, attr, start, end);
4873             }
4874             return val;
4875         };
4876
4877         proto.getAttribute = function(attr) {
4878             var val = null;
4879             var el = this.getEl();
4880
4881             if (attr == 'scroll') {
4882                 val = [ el.scrollLeft, el.scrollTop ];
4883             } else {
4884                 val = superclass.getAttribute.call(this, attr);
4885             }
4886
4887             return val;
4888         };
4889
4890         proto.setAttribute = function(attr, val, unit) {
4891             var el = this.getEl();
4892
4893             if (attr == 'scroll') {
4894                 el.scrollLeft = val[0];
4895                 el.scrollTop = val[1];
4896             } else {
4897                 superclass.setAttribute.call(this, attr, val, unit);
4898             }
4899         };
4900     })();
4901 /*
4902  * Based on:
4903  * Ext JS Library 1.1.1
4904  * Copyright(c) 2006-2007, Ext JS, LLC.
4905  *
4906  * Originally Released Under LGPL - original licence link has changed is not relivant.
4907  *
4908  * Fork - LGPL
4909  * <script type="text/javascript">
4910  */
4911
4912
4913 // nasty IE9 hack - what a pile of crap that is..
4914
4915  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4916     Range.prototype.createContextualFragment = function (html) {
4917         var doc = window.document;
4918         var container = doc.createElement("div");
4919         container.innerHTML = html;
4920         var frag = doc.createDocumentFragment(), n;
4921         while ((n = container.firstChild)) {
4922             frag.appendChild(n);
4923         }
4924         return frag;
4925     };
4926 }
4927
4928 /**
4929  * @class Roo.DomHelper
4930  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4931  * 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>.
4932  * @static
4933  */
4934 Roo.DomHelper = function(){
4935     var tempTableEl = null;
4936     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4937     var tableRe = /^table|tbody|tr|td$/i;
4938     var xmlns = {};
4939     // build as innerHTML where available
4940     /** @ignore */
4941     var createHtml = function(o){
4942         if(typeof o == 'string'){
4943             return o;
4944         }
4945         var b = "";
4946         if(!o.tag){
4947             o.tag = "div";
4948         }
4949         b += "<" + o.tag;
4950         for(var attr in o){
4951             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4952             if(attr == "style"){
4953                 var s = o["style"];
4954                 if(typeof s == "function"){
4955                     s = s.call();
4956                 }
4957                 if(typeof s == "string"){
4958                     b += ' style="' + s + '"';
4959                 }else if(typeof s == "object"){
4960                     b += ' style="';
4961                     for(var key in s){
4962                         if(typeof s[key] != "function"){
4963                             b += key + ":" + s[key] + ";";
4964                         }
4965                     }
4966                     b += '"';
4967                 }
4968             }else{
4969                 if(attr == "cls"){
4970                     b += ' class="' + o["cls"] + '"';
4971                 }else if(attr == "htmlFor"){
4972                     b += ' for="' + o["htmlFor"] + '"';
4973                 }else{
4974                     b += " " + attr + '="' + o[attr] + '"';
4975                 }
4976             }
4977         }
4978         if(emptyTags.test(o.tag)){
4979             b += "/>";
4980         }else{
4981             b += ">";
4982             var cn = o.children || o.cn;
4983             if(cn){
4984                 //http://bugs.kde.org/show_bug.cgi?id=71506
4985                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4986                     for(var i = 0, len = cn.length; i < len; i++) {
4987                         b += createHtml(cn[i], b);
4988                     }
4989                 }else{
4990                     b += createHtml(cn, b);
4991                 }
4992             }
4993             if(o.html){
4994                 b += o.html;
4995             }
4996             b += "</" + o.tag + ">";
4997         }
4998         return b;
4999     };
5000
5001     // build as dom
5002     /** @ignore */
5003     var createDom = function(o, parentNode){
5004          
5005         // defininition craeted..
5006         var ns = false;
5007         if (o.ns && o.ns != 'html') {
5008                
5009             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5010                 xmlns[o.ns] = o.xmlns;
5011                 ns = o.xmlns;
5012             }
5013             if (typeof(xmlns[o.ns]) == 'undefined') {
5014                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5015             }
5016             ns = xmlns[o.ns];
5017         }
5018         
5019         
5020         if (typeof(o) == 'string') {
5021             return parentNode.appendChild(document.createTextNode(o));
5022         }
5023         o.tag = o.tag || div;
5024         if (o.ns && Roo.isIE) {
5025             ns = false;
5026             o.tag = o.ns + ':' + o.tag;
5027             
5028         }
5029         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5030         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5031         for(var attr in o){
5032             
5033             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5034                     attr == "style" || typeof o[attr] == "function") { continue; }
5035                     
5036             if(attr=="cls" && Roo.isIE){
5037                 el.className = o["cls"];
5038             }else{
5039                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5040                 else { 
5041                     el[attr] = o[attr];
5042                 }
5043             }
5044         }
5045         Roo.DomHelper.applyStyles(el, o.style);
5046         var cn = o.children || o.cn;
5047         if(cn){
5048             //http://bugs.kde.org/show_bug.cgi?id=71506
5049              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5050                 for(var i = 0, len = cn.length; i < len; i++) {
5051                     createDom(cn[i], el);
5052                 }
5053             }else{
5054                 createDom(cn, el);
5055             }
5056         }
5057         if(o.html){
5058             el.innerHTML = o.html;
5059         }
5060         if(parentNode){
5061            parentNode.appendChild(el);
5062         }
5063         return el;
5064     };
5065
5066     var ieTable = function(depth, s, h, e){
5067         tempTableEl.innerHTML = [s, h, e].join('');
5068         var i = -1, el = tempTableEl;
5069         while(++i < depth && el.firstChild){
5070             el = el.firstChild;
5071         }
5072         return el;
5073     };
5074
5075     // kill repeat to save bytes
5076     var ts = '<table>',
5077         te = '</table>',
5078         tbs = ts+'<tbody>',
5079         tbe = '</tbody>'+te,
5080         trs = tbs + '<tr>',
5081         tre = '</tr>'+tbe;
5082
5083     /**
5084      * @ignore
5085      * Nasty code for IE's broken table implementation
5086      */
5087     var insertIntoTable = function(tag, where, el, html){
5088         if(!tempTableEl){
5089             tempTableEl = document.createElement('div');
5090         }
5091         var node;
5092         var before = null;
5093         if(tag == 'td'){
5094             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5095                 return;
5096             }
5097             if(where == 'beforebegin'){
5098                 before = el;
5099                 el = el.parentNode;
5100             } else{
5101                 before = el.nextSibling;
5102                 el = el.parentNode;
5103             }
5104             node = ieTable(4, trs, html, tre);
5105         }
5106         else if(tag == 'tr'){
5107             if(where == 'beforebegin'){
5108                 before = el;
5109                 el = el.parentNode;
5110                 node = ieTable(3, tbs, html, tbe);
5111             } else if(where == 'afterend'){
5112                 before = el.nextSibling;
5113                 el = el.parentNode;
5114                 node = ieTable(3, tbs, html, tbe);
5115             } else{ // INTO a TR
5116                 if(where == 'afterbegin'){
5117                     before = el.firstChild;
5118                 }
5119                 node = ieTable(4, trs, html, tre);
5120             }
5121         } else if(tag == 'tbody'){
5122             if(where == 'beforebegin'){
5123                 before = el;
5124                 el = el.parentNode;
5125                 node = ieTable(2, ts, html, te);
5126             } else if(where == 'afterend'){
5127                 before = el.nextSibling;
5128                 el = el.parentNode;
5129                 node = ieTable(2, ts, html, te);
5130             } else{
5131                 if(where == 'afterbegin'){
5132                     before = el.firstChild;
5133                 }
5134                 node = ieTable(3, tbs, html, tbe);
5135             }
5136         } else{ // TABLE
5137             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5138                 return;
5139             }
5140             if(where == 'afterbegin'){
5141                 before = el.firstChild;
5142             }
5143             node = ieTable(2, ts, html, te);
5144         }
5145         el.insertBefore(node, before);
5146         return node;
5147     };
5148
5149     return {
5150     /** True to force the use of DOM instead of html fragments @type Boolean */
5151     useDom : false,
5152
5153     /**
5154      * Returns the markup for the passed Element(s) config
5155      * @param {Object} o The Dom object spec (and children)
5156      * @return {String}
5157      */
5158     markup : function(o){
5159         return createHtml(o);
5160     },
5161
5162     /**
5163      * Applies a style specification to an element
5164      * @param {String/HTMLElement} el The element to apply styles to
5165      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5166      * a function which returns such a specification.
5167      */
5168     applyStyles : function(el, styles){
5169         if(styles){
5170            el = Roo.fly(el);
5171            if(typeof styles == "string"){
5172                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5173                var matches;
5174                while ((matches = re.exec(styles)) != null){
5175                    el.setStyle(matches[1], matches[2]);
5176                }
5177            }else if (typeof styles == "object"){
5178                for (var style in styles){
5179                   el.setStyle(style, styles[style]);
5180                }
5181            }else if (typeof styles == "function"){
5182                 Roo.DomHelper.applyStyles(el, styles.call());
5183            }
5184         }
5185     },
5186
5187     /**
5188      * Inserts an HTML fragment into the Dom
5189      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5190      * @param {HTMLElement} el The context element
5191      * @param {String} html The HTML fragmenet
5192      * @return {HTMLElement} The new node
5193      */
5194     insertHtml : function(where, el, html){
5195         where = where.toLowerCase();
5196         if(el.insertAdjacentHTML){
5197             if(tableRe.test(el.tagName)){
5198                 var rs;
5199                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5200                     return rs;
5201                 }
5202             }
5203             switch(where){
5204                 case "beforebegin":
5205                     el.insertAdjacentHTML('BeforeBegin', html);
5206                     return el.previousSibling;
5207                 case "afterbegin":
5208                     el.insertAdjacentHTML('AfterBegin', html);
5209                     return el.firstChild;
5210                 case "beforeend":
5211                     el.insertAdjacentHTML('BeforeEnd', html);
5212                     return el.lastChild;
5213                 case "afterend":
5214                     el.insertAdjacentHTML('AfterEnd', html);
5215                     return el.nextSibling;
5216             }
5217             throw 'Illegal insertion point -> "' + where + '"';
5218         }
5219         var range = el.ownerDocument.createRange();
5220         var frag;
5221         switch(where){
5222              case "beforebegin":
5223                 range.setStartBefore(el);
5224                 frag = range.createContextualFragment(html);
5225                 el.parentNode.insertBefore(frag, el);
5226                 return el.previousSibling;
5227              case "afterbegin":
5228                 if(el.firstChild){
5229                     range.setStartBefore(el.firstChild);
5230                     frag = range.createContextualFragment(html);
5231                     el.insertBefore(frag, el.firstChild);
5232                     return el.firstChild;
5233                 }else{
5234                     el.innerHTML = html;
5235                     return el.firstChild;
5236                 }
5237             case "beforeend":
5238                 if(el.lastChild){
5239                     range.setStartAfter(el.lastChild);
5240                     frag = range.createContextualFragment(html);
5241                     el.appendChild(frag);
5242                     return el.lastChild;
5243                 }else{
5244                     el.innerHTML = html;
5245                     return el.lastChild;
5246                 }
5247             case "afterend":
5248                 range.setStartAfter(el);
5249                 frag = range.createContextualFragment(html);
5250                 el.parentNode.insertBefore(frag, el.nextSibling);
5251                 return el.nextSibling;
5252             }
5253             throw 'Illegal insertion point -> "' + where + '"';
5254     },
5255
5256     /**
5257      * Creates new Dom element(s) and inserts them before el
5258      * @param {String/HTMLElement/Element} el The context element
5259      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5260      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5261      * @return {HTMLElement/Roo.Element} The new node
5262      */
5263     insertBefore : function(el, o, returnElement){
5264         return this.doInsert(el, o, returnElement, "beforeBegin");
5265     },
5266
5267     /**
5268      * Creates new Dom element(s) and inserts them after el
5269      * @param {String/HTMLElement/Element} el The context element
5270      * @param {Object} o The Dom object spec (and children)
5271      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5272      * @return {HTMLElement/Roo.Element} The new node
5273      */
5274     insertAfter : function(el, o, returnElement){
5275         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5276     },
5277
5278     /**
5279      * Creates new Dom element(s) and inserts them as the first child of el
5280      * @param {String/HTMLElement/Element} el The context element
5281      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5282      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5283      * @return {HTMLElement/Roo.Element} The new node
5284      */
5285     insertFirst : function(el, o, returnElement){
5286         return this.doInsert(el, o, returnElement, "afterBegin");
5287     },
5288
5289     // private
5290     doInsert : function(el, o, returnElement, pos, sibling){
5291         el = Roo.getDom(el);
5292         var newNode;
5293         if(this.useDom || o.ns){
5294             newNode = createDom(o, null);
5295             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5296         }else{
5297             var html = createHtml(o);
5298             newNode = this.insertHtml(pos, el, html);
5299         }
5300         return returnElement ? Roo.get(newNode, true) : newNode;
5301     },
5302
5303     /**
5304      * Creates new Dom element(s) and appends them to el
5305      * @param {String/HTMLElement/Element} el The context element
5306      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5307      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5308      * @return {HTMLElement/Roo.Element} The new node
5309      */
5310     append : function(el, o, returnElement){
5311         el = Roo.getDom(el);
5312         var newNode;
5313         if(this.useDom || o.ns){
5314             newNode = createDom(o, null);
5315             el.appendChild(newNode);
5316         }else{
5317             var html = createHtml(o);
5318             newNode = this.insertHtml("beforeEnd", el, html);
5319         }
5320         return returnElement ? Roo.get(newNode, true) : newNode;
5321     },
5322
5323     /**
5324      * Creates new Dom element(s) and overwrites the contents of el with them
5325      * @param {String/HTMLElement/Element} el The context element
5326      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5327      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5328      * @return {HTMLElement/Roo.Element} The new node
5329      */
5330     overwrite : function(el, o, returnElement){
5331         el = Roo.getDom(el);
5332         if (o.ns) {
5333           
5334             while (el.childNodes.length) {
5335                 el.removeChild(el.firstChild);
5336             }
5337             createDom(o, el);
5338         } else {
5339             el.innerHTML = createHtml(o);   
5340         }
5341         
5342         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5343     },
5344
5345     /**
5346      * Creates a new Roo.DomHelper.Template from the Dom object spec
5347      * @param {Object} o The Dom object spec (and children)
5348      * @return {Roo.DomHelper.Template} The new template
5349      */
5350     createTemplate : function(o){
5351         var html = createHtml(o);
5352         return new Roo.Template(html);
5353     }
5354     };
5355 }();
5356 /*
5357  * Based on:
5358  * Ext JS Library 1.1.1
5359  * Copyright(c) 2006-2007, Ext JS, LLC.
5360  *
5361  * Originally Released Under LGPL - original licence link has changed is not relivant.
5362  *
5363  * Fork - LGPL
5364  * <script type="text/javascript">
5365  */
5366  
5367 /**
5368 * @class Roo.Template
5369 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5370 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5371 * Usage:
5372 <pre><code>
5373 var t = new Roo.Template({
5374     html :  '&lt;div name="{id}"&gt;' + 
5375         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5376         '&lt;/div&gt;',
5377     myformat: function (value, allValues) {
5378         return 'XX' + value;
5379     }
5380 });
5381 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5382 </code></pre>
5383 * For more information see this blog post with examples:
5384 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5385      - Create Elements using DOM, HTML fragments and Templates</a>. 
5386 * @constructor
5387 * @param {Object} cfg - Configuration object.
5388 */
5389 Roo.Template = function(cfg){
5390     // BC!
5391     if(cfg instanceof Array){
5392         cfg = cfg.join("");
5393     }else if(arguments.length > 1){
5394         cfg = Array.prototype.join.call(arguments, "");
5395     }
5396     
5397     
5398     if (typeof(cfg) == 'object') {
5399         Roo.apply(this,cfg)
5400     } else {
5401         // bc
5402         this.html = cfg;
5403     }
5404     if (this.url) {
5405         this.load();
5406     }
5407     
5408 };
5409 Roo.Template.prototype = {
5410     
5411     /**
5412      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5413      */
5414     onLoad : false,
5415     
5416     
5417     /**
5418      * @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..
5419      *                    it should be fixed so that template is observable...
5420      */
5421     url : false,
5422     /**
5423      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5424      */
5425     html : '',
5426     
5427     
5428     compiled : false,
5429     loaded : false,
5430     /**
5431      * Returns an HTML fragment of this template with the specified values applied.
5432      * @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'})
5433      * @return {String} The HTML fragment
5434      */
5435     
5436    
5437     
5438     applyTemplate : function(values){
5439         //Roo.log(["applyTemplate", values]);
5440         try {
5441            
5442             if(this.compiled){
5443                 return this.compiled(values);
5444             }
5445             var useF = this.disableFormats !== true;
5446             var fm = Roo.util.Format, tpl = this;
5447             var fn = function(m, name, format, args){
5448                 if(format && useF){
5449                     if(format.substr(0, 5) == "this."){
5450                         return tpl.call(format.substr(5), values[name], values);
5451                     }else{
5452                         if(args){
5453                             // quoted values are required for strings in compiled templates, 
5454                             // but for non compiled we need to strip them
5455                             // quoted reversed for jsmin
5456                             var re = /^\s*['"](.*)["']\s*$/;
5457                             args = args.split(',');
5458                             for(var i = 0, len = args.length; i < len; i++){
5459                                 args[i] = args[i].replace(re, "$1");
5460                             }
5461                             args = [values[name]].concat(args);
5462                         }else{
5463                             args = [values[name]];
5464                         }
5465                         return fm[format].apply(fm, args);
5466                     }
5467                 }else{
5468                     return values[name] !== undefined ? values[name] : "";
5469                 }
5470             };
5471             return this.html.replace(this.re, fn);
5472         } catch (e) {
5473             Roo.log(e);
5474             throw e;
5475         }
5476          
5477     },
5478     
5479     loading : false,
5480       
5481     load : function ()
5482     {
5483          
5484         if (this.loading) {
5485             return;
5486         }
5487         var _t = this;
5488         
5489         this.loading = true;
5490         this.compiled = false;
5491         
5492         var cx = new Roo.data.Connection();
5493         cx.request({
5494             url : this.url,
5495             method : 'GET',
5496             success : function (response) {
5497                 _t.loading = false;
5498                 _t.url = false;
5499                 
5500                 _t.set(response.responseText,true);
5501                 _t.loaded = true;
5502                 if (_t.onLoad) {
5503                     _t.onLoad();
5504                 }
5505              },
5506             failure : function(response) {
5507                 Roo.log("Template failed to load from " + _t.url);
5508                 _t.loading = false;
5509             }
5510         });
5511     },
5512
5513     /**
5514      * Sets the HTML used as the template and optionally compiles it.
5515      * @param {String} html
5516      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5517      * @return {Roo.Template} this
5518      */
5519     set : function(html, compile){
5520         this.html = html;
5521         this.compiled = false;
5522         if(compile){
5523             this.compile();
5524         }
5525         return this;
5526     },
5527     
5528     /**
5529      * True to disable format functions (defaults to false)
5530      * @type Boolean
5531      */
5532     disableFormats : false,
5533     
5534     /**
5535     * The regular expression used to match template variables 
5536     * @type RegExp
5537     * @property 
5538     */
5539     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5540     
5541     /**
5542      * Compiles the template into an internal function, eliminating the RegEx overhead.
5543      * @return {Roo.Template} this
5544      */
5545     compile : function(){
5546         var fm = Roo.util.Format;
5547         var useF = this.disableFormats !== true;
5548         var sep = Roo.isGecko ? "+" : ",";
5549         var fn = function(m, name, format, args){
5550             if(format && useF){
5551                 args = args ? ',' + args : "";
5552                 if(format.substr(0, 5) != "this."){
5553                     format = "fm." + format + '(';
5554                 }else{
5555                     format = 'this.call("'+ format.substr(5) + '", ';
5556                     args = ", values";
5557                 }
5558             }else{
5559                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5560             }
5561             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5562         };
5563         var body;
5564         // branched to use + in gecko and [].join() in others
5565         if(Roo.isGecko){
5566             body = "this.compiled = function(values){ return '" +
5567                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5568                     "';};";
5569         }else{
5570             body = ["this.compiled = function(values){ return ['"];
5571             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5572             body.push("'].join('');};");
5573             body = body.join('');
5574         }
5575         /**
5576          * eval:var:values
5577          * eval:var:fm
5578          */
5579         eval(body);
5580         return this;
5581     },
5582     
5583     // private function used to call members
5584     call : function(fnName, value, allValues){
5585         return this[fnName](value, allValues);
5586     },
5587     
5588     /**
5589      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5590      * @param {String/HTMLElement/Roo.Element} el The context element
5591      * @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'})
5592      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5593      * @return {HTMLElement/Roo.Element} The new node or Element
5594      */
5595     insertFirst: function(el, values, returnElement){
5596         return this.doInsert('afterBegin', el, values, returnElement);
5597     },
5598
5599     /**
5600      * Applies the supplied values to the template and inserts the new node(s) before el.
5601      * @param {String/HTMLElement/Roo.Element} el The context element
5602      * @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'})
5603      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5604      * @return {HTMLElement/Roo.Element} The new node or Element
5605      */
5606     insertBefore: function(el, values, returnElement){
5607         return this.doInsert('beforeBegin', el, values, returnElement);
5608     },
5609
5610     /**
5611      * Applies the supplied values to the template and inserts the new node(s) after el.
5612      * @param {String/HTMLElement/Roo.Element} el The context element
5613      * @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'})
5614      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5615      * @return {HTMLElement/Roo.Element} The new node or Element
5616      */
5617     insertAfter : function(el, values, returnElement){
5618         return this.doInsert('afterEnd', el, values, returnElement);
5619     },
5620     
5621     /**
5622      * Applies the supplied values to the template and appends the new node(s) to el.
5623      * @param {String/HTMLElement/Roo.Element} el The context element
5624      * @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'})
5625      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5626      * @return {HTMLElement/Roo.Element} The new node or Element
5627      */
5628     append : function(el, values, returnElement){
5629         return this.doInsert('beforeEnd', el, values, returnElement);
5630     },
5631
5632     doInsert : function(where, el, values, returnEl){
5633         el = Roo.getDom(el);
5634         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5635         return returnEl ? Roo.get(newNode, true) : newNode;
5636     },
5637
5638     /**
5639      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5640      * @param {String/HTMLElement/Roo.Element} el The context element
5641      * @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'})
5642      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5643      * @return {HTMLElement/Roo.Element} The new node or Element
5644      */
5645     overwrite : function(el, values, returnElement){
5646         el = Roo.getDom(el);
5647         el.innerHTML = this.applyTemplate(values);
5648         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5649     }
5650 };
5651 /**
5652  * Alias for {@link #applyTemplate}
5653  * @method
5654  */
5655 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5656
5657 // backwards compat
5658 Roo.DomHelper.Template = Roo.Template;
5659
5660 /**
5661  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5662  * @param {String/HTMLElement} el A DOM element or its id
5663  * @returns {Roo.Template} The created template
5664  * @static
5665  */
5666 Roo.Template.from = function(el){
5667     el = Roo.getDom(el);
5668     return new Roo.Template(el.value || el.innerHTML);
5669 };/*
5670  * Based on:
5671  * Ext JS Library 1.1.1
5672  * Copyright(c) 2006-2007, Ext JS, LLC.
5673  *
5674  * Originally Released Under LGPL - original licence link has changed is not relivant.
5675  *
5676  * Fork - LGPL
5677  * <script type="text/javascript">
5678  */
5679  
5680
5681 /*
5682  * This is code is also distributed under MIT license for use
5683  * with jQuery and prototype JavaScript libraries.
5684  */
5685 /**
5686  * @class Roo.DomQuery
5687 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).
5688 <p>
5689 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>
5690
5691 <p>
5692 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.
5693 </p>
5694 <h4>Element Selectors:</h4>
5695 <ul class="list">
5696     <li> <b>*</b> any element</li>
5697     <li> <b>E</b> an element with the tag E</li>
5698     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5699     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5700     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5701     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5702 </ul>
5703 <h4>Attribute Selectors:</h4>
5704 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5705 <ul class="list">
5706     <li> <b>E[foo]</b> has an attribute "foo"</li>
5707     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5708     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5709     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5710     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5711     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5712     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5713 </ul>
5714 <h4>Pseudo Classes:</h4>
5715 <ul class="list">
5716     <li> <b>E:first-child</b> E is the first child of its parent</li>
5717     <li> <b>E:last-child</b> E is the last child of its parent</li>
5718     <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>
5719     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5720     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5721     <li> <b>E:only-child</b> E is the only child of its parent</li>
5722     <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>
5723     <li> <b>E:first</b> the first E in the resultset</li>
5724     <li> <b>E:last</b> the last E in the resultset</li>
5725     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5726     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5727     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5728     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5729     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5730     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5731     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5732     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5733     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5734 </ul>
5735 <h4>CSS Value Selectors:</h4>
5736 <ul class="list">
5737     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5738     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5739     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5740     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5741     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5742     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5743 </ul>
5744  * @static
5745  */
5746 Roo.DomQuery = function(){
5747     var cache = {}, simpleCache = {}, valueCache = {};
5748     var nonSpace = /\S/;
5749     var trimRe = /^\s+|\s+$/g;
5750     var tplRe = /\{(\d+)\}/g;
5751     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5752     var tagTokenRe = /^(#)?([\w-\*]+)/;
5753     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5754
5755     function child(p, index){
5756         var i = 0;
5757         var n = p.firstChild;
5758         while(n){
5759             if(n.nodeType == 1){
5760                if(++i == index){
5761                    return n;
5762                }
5763             }
5764             n = n.nextSibling;
5765         }
5766         return null;
5767     };
5768
5769     function next(n){
5770         while((n = n.nextSibling) && n.nodeType != 1);
5771         return n;
5772     };
5773
5774     function prev(n){
5775         while((n = n.previousSibling) && n.nodeType != 1);
5776         return n;
5777     };
5778
5779     function children(d){
5780         var n = d.firstChild, ni = -1;
5781             while(n){
5782                 var nx = n.nextSibling;
5783                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5784                     d.removeChild(n);
5785                 }else{
5786                     n.nodeIndex = ++ni;
5787                 }
5788                 n = nx;
5789             }
5790             return this;
5791         };
5792
5793     function byClassName(c, a, v){
5794         if(!v){
5795             return c;
5796         }
5797         var r = [], ri = -1, cn;
5798         for(var i = 0, ci; ci = c[i]; i++){
5799             
5800             
5801             if((' '+
5802                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5803                  +' ').indexOf(v) != -1){
5804                 r[++ri] = ci;
5805             }
5806         }
5807         return r;
5808     };
5809
5810     function attrValue(n, attr){
5811         if(!n.tagName && typeof n.length != "undefined"){
5812             n = n[0];
5813         }
5814         if(!n){
5815             return null;
5816         }
5817         if(attr == "for"){
5818             return n.htmlFor;
5819         }
5820         if(attr == "class" || attr == "className"){
5821             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5822         }
5823         return n.getAttribute(attr) || n[attr];
5824
5825     };
5826
5827     function getNodes(ns, mode, tagName){
5828         var result = [], ri = -1, cs;
5829         if(!ns){
5830             return result;
5831         }
5832         tagName = tagName || "*";
5833         if(typeof ns.getElementsByTagName != "undefined"){
5834             ns = [ns];
5835         }
5836         if(!mode){
5837             for(var i = 0, ni; ni = ns[i]; i++){
5838                 cs = ni.getElementsByTagName(tagName);
5839                 for(var j = 0, ci; ci = cs[j]; j++){
5840                     result[++ri] = ci;
5841                 }
5842             }
5843         }else if(mode == "/" || mode == ">"){
5844             var utag = tagName.toUpperCase();
5845             for(var i = 0, ni, cn; ni = ns[i]; i++){
5846                 cn = ni.children || ni.childNodes;
5847                 for(var j = 0, cj; cj = cn[j]; j++){
5848                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5849                         result[++ri] = cj;
5850                     }
5851                 }
5852             }
5853         }else if(mode == "+"){
5854             var utag = tagName.toUpperCase();
5855             for(var i = 0, n; n = ns[i]; i++){
5856                 while((n = n.nextSibling) && n.nodeType != 1);
5857                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5858                     result[++ri] = n;
5859                 }
5860             }
5861         }else if(mode == "~"){
5862             for(var i = 0, n; n = ns[i]; i++){
5863                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5864                 if(n){
5865                     result[++ri] = n;
5866                 }
5867             }
5868         }
5869         return result;
5870     };
5871
5872     function concat(a, b){
5873         if(b.slice){
5874             return a.concat(b);
5875         }
5876         for(var i = 0, l = b.length; i < l; i++){
5877             a[a.length] = b[i];
5878         }
5879         return a;
5880     }
5881
5882     function byTag(cs, tagName){
5883         if(cs.tagName || cs == document){
5884             cs = [cs];
5885         }
5886         if(!tagName){
5887             return cs;
5888         }
5889         var r = [], ri = -1;
5890         tagName = tagName.toLowerCase();
5891         for(var i = 0, ci; ci = cs[i]; i++){
5892             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5893                 r[++ri] = ci;
5894             }
5895         }
5896         return r;
5897     };
5898
5899     function byId(cs, attr, id){
5900         if(cs.tagName || cs == document){
5901             cs = [cs];
5902         }
5903         if(!id){
5904             return cs;
5905         }
5906         var r = [], ri = -1;
5907         for(var i = 0,ci; ci = cs[i]; i++){
5908             if(ci && ci.id == id){
5909                 r[++ri] = ci;
5910                 return r;
5911             }
5912         }
5913         return r;
5914     };
5915
5916     function byAttribute(cs, attr, value, op, custom){
5917         var r = [], ri = -1, st = custom=="{";
5918         var f = Roo.DomQuery.operators[op];
5919         for(var i = 0, ci; ci = cs[i]; i++){
5920             var a;
5921             if(st){
5922                 a = Roo.DomQuery.getStyle(ci, attr);
5923             }
5924             else if(attr == "class" || attr == "className"){
5925                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5926             }else if(attr == "for"){
5927                 a = ci.htmlFor;
5928             }else if(attr == "href"){
5929                 a = ci.getAttribute("href", 2);
5930             }else{
5931                 a = ci.getAttribute(attr);
5932             }
5933             if((f && f(a, value)) || (!f && a)){
5934                 r[++ri] = ci;
5935             }
5936         }
5937         return r;
5938     };
5939
5940     function byPseudo(cs, name, value){
5941         return Roo.DomQuery.pseudos[name](cs, value);
5942     };
5943
5944     // This is for IE MSXML which does not support expandos.
5945     // IE runs the same speed using setAttribute, however FF slows way down
5946     // and Safari completely fails so they need to continue to use expandos.
5947     var isIE = window.ActiveXObject ? true : false;
5948
5949     // this eval is stop the compressor from
5950     // renaming the variable to something shorter
5951     
5952     /** eval:var:batch */
5953     var batch = 30803; 
5954
5955     var key = 30803;
5956
5957     function nodupIEXml(cs){
5958         var d = ++key;
5959         cs[0].setAttribute("_nodup", d);
5960         var r = [cs[0]];
5961         for(var i = 1, len = cs.length; i < len; i++){
5962             var c = cs[i];
5963             if(!c.getAttribute("_nodup") != d){
5964                 c.setAttribute("_nodup", d);
5965                 r[r.length] = c;
5966             }
5967         }
5968         for(var i = 0, len = cs.length; i < len; i++){
5969             cs[i].removeAttribute("_nodup");
5970         }
5971         return r;
5972     }
5973
5974     function nodup(cs){
5975         if(!cs){
5976             return [];
5977         }
5978         var len = cs.length, c, i, r = cs, cj, ri = -1;
5979         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5980             return cs;
5981         }
5982         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5983             return nodupIEXml(cs);
5984         }
5985         var d = ++key;
5986         cs[0]._nodup = d;
5987         for(i = 1; c = cs[i]; i++){
5988             if(c._nodup != d){
5989                 c._nodup = d;
5990             }else{
5991                 r = [];
5992                 for(var j = 0; j < i; j++){
5993                     r[++ri] = cs[j];
5994                 }
5995                 for(j = i+1; cj = cs[j]; j++){
5996                     if(cj._nodup != d){
5997                         cj._nodup = d;
5998                         r[++ri] = cj;
5999                     }
6000                 }
6001                 return r;
6002             }
6003         }
6004         return r;
6005     }
6006
6007     function quickDiffIEXml(c1, c2){
6008         var d = ++key;
6009         for(var i = 0, len = c1.length; i < len; i++){
6010             c1[i].setAttribute("_qdiff", d);
6011         }
6012         var r = [];
6013         for(var i = 0, len = c2.length; i < len; i++){
6014             if(c2[i].getAttribute("_qdiff") != d){
6015                 r[r.length] = c2[i];
6016             }
6017         }
6018         for(var i = 0, len = c1.length; i < len; i++){
6019            c1[i].removeAttribute("_qdiff");
6020         }
6021         return r;
6022     }
6023
6024     function quickDiff(c1, c2){
6025         var len1 = c1.length;
6026         if(!len1){
6027             return c2;
6028         }
6029         if(isIE && c1[0].selectSingleNode){
6030             return quickDiffIEXml(c1, c2);
6031         }
6032         var d = ++key;
6033         for(var i = 0; i < len1; i++){
6034             c1[i]._qdiff = d;
6035         }
6036         var r = [];
6037         for(var i = 0, len = c2.length; i < len; i++){
6038             if(c2[i]._qdiff != d){
6039                 r[r.length] = c2[i];
6040             }
6041         }
6042         return r;
6043     }
6044
6045     function quickId(ns, mode, root, id){
6046         if(ns == root){
6047            var d = root.ownerDocument || root;
6048            return d.getElementById(id);
6049         }
6050         ns = getNodes(ns, mode, "*");
6051         return byId(ns, null, id);
6052     }
6053
6054     return {
6055         getStyle : function(el, name){
6056             return Roo.fly(el).getStyle(name);
6057         },
6058         /**
6059          * Compiles a selector/xpath query into a reusable function. The returned function
6060          * takes one parameter "root" (optional), which is the context node from where the query should start.
6061          * @param {String} selector The selector/xpath query
6062          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6063          * @return {Function}
6064          */
6065         compile : function(path, type){
6066             type = type || "select";
6067             
6068             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6069             var q = path, mode, lq;
6070             var tk = Roo.DomQuery.matchers;
6071             var tklen = tk.length;
6072             var mm;
6073
6074             // accept leading mode switch
6075             var lmode = q.match(modeRe);
6076             if(lmode && lmode[1]){
6077                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6078                 q = q.replace(lmode[1], "");
6079             }
6080             // strip leading slashes
6081             while(path.substr(0, 1)=="/"){
6082                 path = path.substr(1);
6083             }
6084
6085             while(q && lq != q){
6086                 lq = q;
6087                 var tm = q.match(tagTokenRe);
6088                 if(type == "select"){
6089                     if(tm){
6090                         if(tm[1] == "#"){
6091                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6092                         }else{
6093                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6094                         }
6095                         q = q.replace(tm[0], "");
6096                     }else if(q.substr(0, 1) != '@'){
6097                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6098                     }
6099                 }else{
6100                     if(tm){
6101                         if(tm[1] == "#"){
6102                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6103                         }else{
6104                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6105                         }
6106                         q = q.replace(tm[0], "");
6107                     }
6108                 }
6109                 while(!(mm = q.match(modeRe))){
6110                     var matched = false;
6111                     for(var j = 0; j < tklen; j++){
6112                         var t = tk[j];
6113                         var m = q.match(t.re);
6114                         if(m){
6115                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6116                                                     return m[i];
6117                                                 });
6118                             q = q.replace(m[0], "");
6119                             matched = true;
6120                             break;
6121                         }
6122                     }
6123                     // prevent infinite loop on bad selector
6124                     if(!matched){
6125                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6126                     }
6127                 }
6128                 if(mm[1]){
6129                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6130                     q = q.replace(mm[1], "");
6131                 }
6132             }
6133             fn[fn.length] = "return nodup(n);\n}";
6134             
6135              /** 
6136               * list of variables that need from compression as they are used by eval.
6137              *  eval:var:batch 
6138              *  eval:var:nodup
6139              *  eval:var:byTag
6140              *  eval:var:ById
6141              *  eval:var:getNodes
6142              *  eval:var:quickId
6143              *  eval:var:mode
6144              *  eval:var:root
6145              *  eval:var:n
6146              *  eval:var:byClassName
6147              *  eval:var:byPseudo
6148              *  eval:var:byAttribute
6149              *  eval:var:attrValue
6150              * 
6151              **/ 
6152             eval(fn.join(""));
6153             return f;
6154         },
6155
6156         /**
6157          * Selects a group of elements.
6158          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6159          * @param {Node} root (optional) The start of the query (defaults to document).
6160          * @return {Array}
6161          */
6162         select : function(path, root, type){
6163             if(!root || root == document){
6164                 root = document;
6165             }
6166             if(typeof root == "string"){
6167                 root = document.getElementById(root);
6168             }
6169             var paths = path.split(",");
6170             var results = [];
6171             for(var i = 0, len = paths.length; i < len; i++){
6172                 var p = paths[i].replace(trimRe, "");
6173                 if(!cache[p]){
6174                     cache[p] = Roo.DomQuery.compile(p);
6175                     if(!cache[p]){
6176                         throw p + " is not a valid selector";
6177                     }
6178                 }
6179                 var result = cache[p](root);
6180                 if(result && result != document){
6181                     results = results.concat(result);
6182                 }
6183             }
6184             if(paths.length > 1){
6185                 return nodup(results);
6186             }
6187             return results;
6188         },
6189
6190         /**
6191          * Selects a single element.
6192          * @param {String} selector The selector/xpath query
6193          * @param {Node} root (optional) The start of the query (defaults to document).
6194          * @return {Element}
6195          */
6196         selectNode : function(path, root){
6197             return Roo.DomQuery.select(path, root)[0];
6198         },
6199
6200         /**
6201          * Selects the value of a node, optionally replacing null with the defaultValue.
6202          * @param {String} selector The selector/xpath query
6203          * @param {Node} root (optional) The start of the query (defaults to document).
6204          * @param {String} defaultValue
6205          */
6206         selectValue : function(path, root, defaultValue){
6207             path = path.replace(trimRe, "");
6208             if(!valueCache[path]){
6209                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6210             }
6211             var n = valueCache[path](root);
6212             n = n[0] ? n[0] : n;
6213             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6214             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6215         },
6216
6217         /**
6218          * Selects the value of a node, parsing integers and floats.
6219          * @param {String} selector The selector/xpath query
6220          * @param {Node} root (optional) The start of the query (defaults to document).
6221          * @param {Number} defaultValue
6222          * @return {Number}
6223          */
6224         selectNumber : function(path, root, defaultValue){
6225             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6226             return parseFloat(v);
6227         },
6228
6229         /**
6230          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6231          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6232          * @param {String} selector The simple selector to test
6233          * @return {Boolean}
6234          */
6235         is : function(el, ss){
6236             if(typeof el == "string"){
6237                 el = document.getElementById(el);
6238             }
6239             var isArray = (el instanceof Array);
6240             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6241             return isArray ? (result.length == el.length) : (result.length > 0);
6242         },
6243
6244         /**
6245          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6246          * @param {Array} el An array of elements to filter
6247          * @param {String} selector The simple selector to test
6248          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6249          * the selector instead of the ones that match
6250          * @return {Array}
6251          */
6252         filter : function(els, ss, nonMatches){
6253             ss = ss.replace(trimRe, "");
6254             if(!simpleCache[ss]){
6255                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6256             }
6257             var result = simpleCache[ss](els);
6258             return nonMatches ? quickDiff(result, els) : result;
6259         },
6260
6261         /**
6262          * Collection of matching regular expressions and code snippets.
6263          */
6264         matchers : [{
6265                 re: /^\.([\w-]+)/,
6266                 select: 'n = byClassName(n, null, " {1} ");'
6267             }, {
6268                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6269                 select: 'n = byPseudo(n, "{1}", "{2}");'
6270             },{
6271                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6272                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6273             }, {
6274                 re: /^#([\w-]+)/,
6275                 select: 'n = byId(n, null, "{1}");'
6276             },{
6277                 re: /^@([\w-]+)/,
6278                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6279             }
6280         ],
6281
6282         /**
6283          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6284          * 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;.
6285          */
6286         operators : {
6287             "=" : function(a, v){
6288                 return a == v;
6289             },
6290             "!=" : function(a, v){
6291                 return a != v;
6292             },
6293             "^=" : function(a, v){
6294                 return a && a.substr(0, v.length) == v;
6295             },
6296             "$=" : function(a, v){
6297                 return a && a.substr(a.length-v.length) == v;
6298             },
6299             "*=" : function(a, v){
6300                 return a && a.indexOf(v) !== -1;
6301             },
6302             "%=" : function(a, v){
6303                 return (a % v) == 0;
6304             },
6305             "|=" : function(a, v){
6306                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6307             },
6308             "~=" : function(a, v){
6309                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6310             }
6311         },
6312
6313         /**
6314          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6315          * and the argument (if any) supplied in the selector.
6316          */
6317         pseudos : {
6318             "first-child" : function(c){
6319                 var r = [], ri = -1, n;
6320                 for(var i = 0, ci; ci = n = c[i]; i++){
6321                     while((n = n.previousSibling) && n.nodeType != 1);
6322                     if(!n){
6323                         r[++ri] = ci;
6324                     }
6325                 }
6326                 return r;
6327             },
6328
6329             "last-child" : function(c){
6330                 var r = [], ri = -1, n;
6331                 for(var i = 0, ci; ci = n = c[i]; i++){
6332                     while((n = n.nextSibling) && n.nodeType != 1);
6333                     if(!n){
6334                         r[++ri] = ci;
6335                     }
6336                 }
6337                 return r;
6338             },
6339
6340             "nth-child" : function(c, a) {
6341                 var r = [], ri = -1;
6342                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6343                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6344                 for(var i = 0, n; n = c[i]; i++){
6345                     var pn = n.parentNode;
6346                     if (batch != pn._batch) {
6347                         var j = 0;
6348                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6349                             if(cn.nodeType == 1){
6350                                cn.nodeIndex = ++j;
6351                             }
6352                         }
6353                         pn._batch = batch;
6354                     }
6355                     if (f == 1) {
6356                         if (l == 0 || n.nodeIndex == l){
6357                             r[++ri] = n;
6358                         }
6359                     } else if ((n.nodeIndex + l) % f == 0){
6360                         r[++ri] = n;
6361                     }
6362                 }
6363
6364                 return r;
6365             },
6366
6367             "only-child" : function(c){
6368                 var r = [], ri = -1;;
6369                 for(var i = 0, ci; ci = c[i]; i++){
6370                     if(!prev(ci) && !next(ci)){
6371                         r[++ri] = ci;
6372                     }
6373                 }
6374                 return r;
6375             },
6376
6377             "empty" : function(c){
6378                 var r = [], ri = -1;
6379                 for(var i = 0, ci; ci = c[i]; i++){
6380                     var cns = ci.childNodes, j = 0, cn, empty = true;
6381                     while(cn = cns[j]){
6382                         ++j;
6383                         if(cn.nodeType == 1 || cn.nodeType == 3){
6384                             empty = false;
6385                             break;
6386                         }
6387                     }
6388                     if(empty){
6389                         r[++ri] = ci;
6390                     }
6391                 }
6392                 return r;
6393             },
6394
6395             "contains" : function(c, v){
6396                 var r = [], ri = -1;
6397                 for(var i = 0, ci; ci = c[i]; i++){
6398                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6399                         r[++ri] = ci;
6400                     }
6401                 }
6402                 return r;
6403             },
6404
6405             "nodeValue" : function(c, v){
6406                 var r = [], ri = -1;
6407                 for(var i = 0, ci; ci = c[i]; i++){
6408                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6409                         r[++ri] = ci;
6410                     }
6411                 }
6412                 return r;
6413             },
6414
6415             "checked" : function(c){
6416                 var r = [], ri = -1;
6417                 for(var i = 0, ci; ci = c[i]; i++){
6418                     if(ci.checked == true){
6419                         r[++ri] = ci;
6420                     }
6421                 }
6422                 return r;
6423             },
6424
6425             "not" : function(c, ss){
6426                 return Roo.DomQuery.filter(c, ss, true);
6427             },
6428
6429             "odd" : function(c){
6430                 return this["nth-child"](c, "odd");
6431             },
6432
6433             "even" : function(c){
6434                 return this["nth-child"](c, "even");
6435             },
6436
6437             "nth" : function(c, a){
6438                 return c[a-1] || [];
6439             },
6440
6441             "first" : function(c){
6442                 return c[0] || [];
6443             },
6444
6445             "last" : function(c){
6446                 return c[c.length-1] || [];
6447             },
6448
6449             "has" : function(c, ss){
6450                 var s = Roo.DomQuery.select;
6451                 var r = [], ri = -1;
6452                 for(var i = 0, ci; ci = c[i]; i++){
6453                     if(s(ss, ci).length > 0){
6454                         r[++ri] = ci;
6455                     }
6456                 }
6457                 return r;
6458             },
6459
6460             "next" : function(c, ss){
6461                 var is = Roo.DomQuery.is;
6462                 var r = [], ri = -1;
6463                 for(var i = 0, ci; ci = c[i]; i++){
6464                     var n = next(ci);
6465                     if(n && is(n, ss)){
6466                         r[++ri] = ci;
6467                     }
6468                 }
6469                 return r;
6470             },
6471
6472             "prev" : function(c, ss){
6473                 var is = Roo.DomQuery.is;
6474                 var r = [], ri = -1;
6475                 for(var i = 0, ci; ci = c[i]; i++){
6476                     var n = prev(ci);
6477                     if(n && is(n, ss)){
6478                         r[++ri] = ci;
6479                     }
6480                 }
6481                 return r;
6482             }
6483         }
6484     };
6485 }();
6486
6487 /**
6488  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6489  * @param {String} path The selector/xpath query
6490  * @param {Node} root (optional) The start of the query (defaults to document).
6491  * @return {Array}
6492  * @member Roo
6493  * @method query
6494  */
6495 Roo.query = Roo.DomQuery.select;
6496 /*
6497  * Based on:
6498  * Ext JS Library 1.1.1
6499  * Copyright(c) 2006-2007, Ext JS, LLC.
6500  *
6501  * Originally Released Under LGPL - original licence link has changed is not relivant.
6502  *
6503  * Fork - LGPL
6504  * <script type="text/javascript">
6505  */
6506
6507 /**
6508  * @class Roo.util.Observable
6509  * Base class that provides a common interface for publishing events. Subclasses are expected to
6510  * to have a property "events" with all the events defined.<br>
6511  * For example:
6512  * <pre><code>
6513  Employee = function(name){
6514     this.name = name;
6515     this.addEvents({
6516         "fired" : true,
6517         "quit" : true
6518     });
6519  }
6520  Roo.extend(Employee, Roo.util.Observable);
6521 </code></pre>
6522  * @param {Object} config properties to use (incuding events / listeners)
6523  */
6524
6525 Roo.util.Observable = function(cfg){
6526     
6527     cfg = cfg|| {};
6528     this.addEvents(cfg.events || {});
6529     if (cfg.events) {
6530         delete cfg.events; // make sure
6531     }
6532      
6533     Roo.apply(this, cfg);
6534     
6535     if(this.listeners){
6536         this.on(this.listeners);
6537         delete this.listeners;
6538     }
6539 };
6540 Roo.util.Observable.prototype = {
6541     /** 
6542  * @cfg {Object} listeners  list of events and functions to call for this object, 
6543  * For example :
6544  * <pre><code>
6545     listeners :  { 
6546        'click' : function(e) {
6547            ..... 
6548         } ,
6549         .... 
6550     } 
6551   </code></pre>
6552  */
6553     
6554     
6555     /**
6556      * Fires the specified event with the passed parameters (minus the event name).
6557      * @param {String} eventName
6558      * @param {Object...} args Variable number of parameters are passed to handlers
6559      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6560      */
6561     fireEvent : function(){
6562         var ce = this.events[arguments[0].toLowerCase()];
6563         if(typeof ce == "object"){
6564             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6565         }else{
6566             return true;
6567         }
6568     },
6569
6570     // private
6571     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6572
6573     /**
6574      * Appends an event handler to this component
6575      * @param {String}   eventName The type of event to listen for
6576      * @param {Function} handler The method the event invokes
6577      * @param {Object}   scope (optional) The scope in which to execute the handler
6578      * function. The handler function's "this" context.
6579      * @param {Object}   options (optional) An object containing handler configuration
6580      * properties. This may contain any of the following properties:<ul>
6581      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6582      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6583      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6584      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6585      * by the specified number of milliseconds. If the event fires again within that time, the original
6586      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6587      * </ul><br>
6588      * <p>
6589      * <b>Combining Options</b><br>
6590      * Using the options argument, it is possible to combine different types of listeners:<br>
6591      * <br>
6592      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6593                 <pre><code>
6594                 el.on('click', this.onClick, this, {
6595                         single: true,
6596                 delay: 100,
6597                 forumId: 4
6598                 });
6599                 </code></pre>
6600      * <p>
6601      * <b>Attaching multiple handlers in 1 call</b><br>
6602      * The method also allows for a single argument to be passed which is a config object containing properties
6603      * which specify multiple handlers.
6604      * <pre><code>
6605                 el.on({
6606                         'click': {
6607                         fn: this.onClick,
6608                         scope: this,
6609                         delay: 100
6610                 }, 
6611                 'mouseover': {
6612                         fn: this.onMouseOver,
6613                         scope: this
6614                 },
6615                 'mouseout': {
6616                         fn: this.onMouseOut,
6617                         scope: this
6618                 }
6619                 });
6620                 </code></pre>
6621      * <p>
6622      * Or a shorthand syntax which passes the same scope object to all handlers:
6623         <pre><code>
6624                 el.on({
6625                         'click': this.onClick,
6626                 'mouseover': this.onMouseOver,
6627                 'mouseout': this.onMouseOut,
6628                 scope: this
6629                 });
6630                 </code></pre>
6631      */
6632     addListener : function(eventName, fn, scope, o){
6633         if(typeof eventName == "object"){
6634             o = eventName;
6635             for(var e in o){
6636                 if(this.filterOptRe.test(e)){
6637                     continue;
6638                 }
6639                 if(typeof o[e] == "function"){
6640                     // shared options
6641                     this.addListener(e, o[e], o.scope,  o);
6642                 }else{
6643                     // individual options
6644                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6645                 }
6646             }
6647             return;
6648         }
6649         o = (!o || typeof o == "boolean") ? {} : o;
6650         eventName = eventName.toLowerCase();
6651         var ce = this.events[eventName] || true;
6652         if(typeof ce == "boolean"){
6653             ce = new Roo.util.Event(this, eventName);
6654             this.events[eventName] = ce;
6655         }
6656         ce.addListener(fn, scope, o);
6657     },
6658
6659     /**
6660      * Removes a listener
6661      * @param {String}   eventName     The type of event to listen for
6662      * @param {Function} handler        The handler to remove
6663      * @param {Object}   scope  (optional) The scope (this object) for the handler
6664      */
6665     removeListener : function(eventName, fn, scope){
6666         var ce = this.events[eventName.toLowerCase()];
6667         if(typeof ce == "object"){
6668             ce.removeListener(fn, scope);
6669         }
6670     },
6671
6672     /**
6673      * Removes all listeners for this object
6674      */
6675     purgeListeners : function(){
6676         for(var evt in this.events){
6677             if(typeof this.events[evt] == "object"){
6678                  this.events[evt].clearListeners();
6679             }
6680         }
6681     },
6682
6683     relayEvents : function(o, events){
6684         var createHandler = function(ename){
6685             return function(){
6686                  
6687                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6688             };
6689         };
6690         for(var i = 0, len = events.length; i < len; i++){
6691             var ename = events[i];
6692             if(!this.events[ename]){
6693                 this.events[ename] = true;
6694             };
6695             o.on(ename, createHandler(ename), this);
6696         }
6697     },
6698
6699     /**
6700      * Used to define events on this Observable
6701      * @param {Object} object The object with the events defined
6702      */
6703     addEvents : function(o){
6704         if(!this.events){
6705             this.events = {};
6706         }
6707         Roo.applyIf(this.events, o);
6708     },
6709
6710     /**
6711      * Checks to see if this object has any listeners for a specified event
6712      * @param {String} eventName The name of the event to check for
6713      * @return {Boolean} True if the event is being listened for, else false
6714      */
6715     hasListener : function(eventName){
6716         var e = this.events[eventName];
6717         return typeof e == "object" && e.listeners.length > 0;
6718     }
6719 };
6720 /**
6721  * Appends an event handler to this element (shorthand for addListener)
6722  * @param {String}   eventName     The type of event to listen for
6723  * @param {Function} handler        The method the event invokes
6724  * @param {Object}   scope (optional) The scope in which to execute the handler
6725  * function. The handler function's "this" context.
6726  * @param {Object}   options  (optional)
6727  * @method
6728  */
6729 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6730 /**
6731  * Removes a listener (shorthand for removeListener)
6732  * @param {String}   eventName     The type of event to listen for
6733  * @param {Function} handler        The handler to remove
6734  * @param {Object}   scope  (optional) The scope (this object) for the handler
6735  * @method
6736  */
6737 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6738
6739 /**
6740  * Starts capture on the specified Observable. All events will be passed
6741  * to the supplied function with the event name + standard signature of the event
6742  * <b>before</b> the event is fired. If the supplied function returns false,
6743  * the event will not fire.
6744  * @param {Observable} o The Observable to capture
6745  * @param {Function} fn The function to call
6746  * @param {Object} scope (optional) The scope (this object) for the fn
6747  * @static
6748  */
6749 Roo.util.Observable.capture = function(o, fn, scope){
6750     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6751 };
6752
6753 /**
6754  * Removes <b>all</b> added captures from the Observable.
6755  * @param {Observable} o The Observable to release
6756  * @static
6757  */
6758 Roo.util.Observable.releaseCapture = function(o){
6759     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6760 };
6761
6762 (function(){
6763
6764     var createBuffered = function(h, o, scope){
6765         var task = new Roo.util.DelayedTask();
6766         return function(){
6767             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6768         };
6769     };
6770
6771     var createSingle = function(h, e, fn, scope){
6772         return function(){
6773             e.removeListener(fn, scope);
6774             return h.apply(scope, arguments);
6775         };
6776     };
6777
6778     var createDelayed = function(h, o, scope){
6779         return function(){
6780             var args = Array.prototype.slice.call(arguments, 0);
6781             setTimeout(function(){
6782                 h.apply(scope, args);
6783             }, o.delay || 10);
6784         };
6785     };
6786
6787     Roo.util.Event = function(obj, name){
6788         this.name = name;
6789         this.obj = obj;
6790         this.listeners = [];
6791     };
6792
6793     Roo.util.Event.prototype = {
6794         addListener : function(fn, scope, options){
6795             var o = options || {};
6796             scope = scope || this.obj;
6797             if(!this.isListening(fn, scope)){
6798                 var l = {fn: fn, scope: scope, options: o};
6799                 var h = fn;
6800                 if(o.delay){
6801                     h = createDelayed(h, o, scope);
6802                 }
6803                 if(o.single){
6804                     h = createSingle(h, this, fn, scope);
6805                 }
6806                 if(o.buffer){
6807                     h = createBuffered(h, o, scope);
6808                 }
6809                 l.fireFn = h;
6810                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6811                     this.listeners.push(l);
6812                 }else{
6813                     this.listeners = this.listeners.slice(0);
6814                     this.listeners.push(l);
6815                 }
6816             }
6817         },
6818
6819         findListener : function(fn, scope){
6820             scope = scope || this.obj;
6821             var ls = this.listeners;
6822             for(var i = 0, len = ls.length; i < len; i++){
6823                 var l = ls[i];
6824                 if(l.fn == fn && l.scope == scope){
6825                     return i;
6826                 }
6827             }
6828             return -1;
6829         },
6830
6831         isListening : function(fn, scope){
6832             return this.findListener(fn, scope) != -1;
6833         },
6834
6835         removeListener : function(fn, scope){
6836             var index;
6837             if((index = this.findListener(fn, scope)) != -1){
6838                 if(!this.firing){
6839                     this.listeners.splice(index, 1);
6840                 }else{
6841                     this.listeners = this.listeners.slice(0);
6842                     this.listeners.splice(index, 1);
6843                 }
6844                 return true;
6845             }
6846             return false;
6847         },
6848
6849         clearListeners : function(){
6850             this.listeners = [];
6851         },
6852
6853         fire : function(){
6854             var ls = this.listeners, scope, len = ls.length;
6855             if(len > 0){
6856                 this.firing = true;
6857                 var args = Array.prototype.slice.call(arguments, 0);                
6858                 for(var i = 0; i < len; i++){
6859                     var l = ls[i];
6860                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6861                         this.firing = false;
6862                         return false;
6863                     }
6864                 }
6865                 this.firing = false;
6866             }
6867             return true;
6868         }
6869     };
6870 })();/*
6871  * RooJS Library 
6872  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6873  *
6874  * Licence LGPL 
6875  *
6876  */
6877  
6878 /**
6879  * @class Roo.Document
6880  * @extends Roo.util.Observable
6881  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6882  * 
6883  * @param {Object} config the methods and properties of the 'base' class for the application.
6884  * 
6885  *  Generic Page handler - implement this to start your app..
6886  * 
6887  * eg.
6888  *  MyProject = new Roo.Document({
6889         events : {
6890             'load' : true // your events..
6891         },
6892         listeners : {
6893             'ready' : function() {
6894                 // fired on Roo.onReady()
6895             }
6896         }
6897  * 
6898  */
6899 Roo.Document = function(cfg) {
6900      
6901     this.addEvents({ 
6902         'ready' : true
6903     });
6904     Roo.util.Observable.call(this,cfg);
6905     
6906     var _this = this;
6907     
6908     Roo.onReady(function() {
6909         _this.fireEvent('ready');
6910     },null,false);
6911     
6912     
6913 }
6914
6915 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6916  * Based on:
6917  * Ext JS Library 1.1.1
6918  * Copyright(c) 2006-2007, Ext JS, LLC.
6919  *
6920  * Originally Released Under LGPL - original licence link has changed is not relivant.
6921  *
6922  * Fork - LGPL
6923  * <script type="text/javascript">
6924  */
6925
6926 /**
6927  * @class Roo.EventManager
6928  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6929  * several useful events directly.
6930  * See {@link Roo.EventObject} for more details on normalized event objects.
6931  * @static
6932  */
6933 Roo.EventManager = function(){
6934     var docReadyEvent, docReadyProcId, docReadyState = false;
6935     var resizeEvent, resizeTask, textEvent, textSize;
6936     var E = Roo.lib.Event;
6937     var D = Roo.lib.Dom;
6938
6939     
6940     
6941
6942     var fireDocReady = function(){
6943         if(!docReadyState){
6944             docReadyState = true;
6945             Roo.isReady = true;
6946             if(docReadyProcId){
6947                 clearInterval(docReadyProcId);
6948             }
6949             if(Roo.isGecko || Roo.isOpera) {
6950                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6951             }
6952             if(Roo.isIE){
6953                 var defer = document.getElementById("ie-deferred-loader");
6954                 if(defer){
6955                     defer.onreadystatechange = null;
6956                     defer.parentNode.removeChild(defer);
6957                 }
6958             }
6959             if(docReadyEvent){
6960                 docReadyEvent.fire();
6961                 docReadyEvent.clearListeners();
6962             }
6963         }
6964     };
6965     
6966     var initDocReady = function(){
6967         docReadyEvent = new Roo.util.Event();
6968         if(Roo.isGecko || Roo.isOpera) {
6969             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6970         }else if(Roo.isIE){
6971             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6972             var defer = document.getElementById("ie-deferred-loader");
6973             defer.onreadystatechange = function(){
6974                 if(this.readyState == "complete"){
6975                     fireDocReady();
6976                 }
6977             };
6978         }else if(Roo.isSafari){ 
6979             docReadyProcId = setInterval(function(){
6980                 var rs = document.readyState;
6981                 if(rs == "complete") {
6982                     fireDocReady();     
6983                  }
6984             }, 10);
6985         }
6986         // no matter what, make sure it fires on load
6987         E.on(window, "load", fireDocReady);
6988     };
6989
6990     var createBuffered = function(h, o){
6991         var task = new Roo.util.DelayedTask(h);
6992         return function(e){
6993             // create new event object impl so new events don't wipe out properties
6994             e = new Roo.EventObjectImpl(e);
6995             task.delay(o.buffer, h, null, [e]);
6996         };
6997     };
6998
6999     var createSingle = function(h, el, ename, fn){
7000         return function(e){
7001             Roo.EventManager.removeListener(el, ename, fn);
7002             h(e);
7003         };
7004     };
7005
7006     var createDelayed = function(h, o){
7007         return function(e){
7008             // create new event object impl so new events don't wipe out properties
7009             e = new Roo.EventObjectImpl(e);
7010             setTimeout(function(){
7011                 h(e);
7012             }, o.delay || 10);
7013         };
7014     };
7015     var transitionEndVal = false;
7016     
7017     var transitionEnd = function()
7018     {
7019         if (transitionEndVal) {
7020             return transitionEndVal;
7021         }
7022         var el = document.createElement('div');
7023
7024         var transEndEventNames = {
7025             WebkitTransition : 'webkitTransitionEnd',
7026             MozTransition    : 'transitionend',
7027             OTransition      : 'oTransitionEnd otransitionend',
7028             transition       : 'transitionend'
7029         };
7030     
7031         for (var name in transEndEventNames) {
7032             if (el.style[name] !== undefined) {
7033                 transitionEndVal = transEndEventNames[name];
7034                 return  transitionEndVal ;
7035             }
7036         }
7037     }
7038     
7039   
7040
7041     var listen = function(element, ename, opt, fn, scope)
7042     {
7043         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7044         fn = fn || o.fn; scope = scope || o.scope;
7045         var el = Roo.getDom(element);
7046         
7047         
7048         if(!el){
7049             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7050         }
7051         
7052         if (ename == 'transitionend') {
7053             ename = transitionEnd();
7054         }
7055         var h = function(e){
7056             e = Roo.EventObject.setEvent(e);
7057             var t;
7058             if(o.delegate){
7059                 t = e.getTarget(o.delegate, el);
7060                 if(!t){
7061                     return;
7062                 }
7063             }else{
7064                 t = e.target;
7065             }
7066             if(o.stopEvent === true){
7067                 e.stopEvent();
7068             }
7069             if(o.preventDefault === true){
7070                e.preventDefault();
7071             }
7072             if(o.stopPropagation === true){
7073                 e.stopPropagation();
7074             }
7075
7076             if(o.normalized === false){
7077                 e = e.browserEvent;
7078             }
7079
7080             fn.call(scope || el, e, t, o);
7081         };
7082         if(o.delay){
7083             h = createDelayed(h, o);
7084         }
7085         if(o.single){
7086             h = createSingle(h, el, ename, fn);
7087         }
7088         if(o.buffer){
7089             h = createBuffered(h, o);
7090         }
7091         
7092         fn._handlers = fn._handlers || [];
7093         
7094         
7095         fn._handlers.push([Roo.id(el), ename, h]);
7096         
7097         
7098          
7099         E.on(el, ename, h); // this adds the actuall listener to the object..
7100         
7101         
7102         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7103             el.addEventListener("DOMMouseScroll", h, false);
7104             E.on(window, 'unload', function(){
7105                 el.removeEventListener("DOMMouseScroll", h, false);
7106             });
7107         }
7108         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7109             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7110         }
7111         return h;
7112     };
7113
7114     var stopListening = function(el, ename, fn){
7115         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7116         if(hds){
7117             for(var i = 0, len = hds.length; i < len; i++){
7118                 var h = hds[i];
7119                 if(h[0] == id && h[1] == ename){
7120                     hd = h[2];
7121                     hds.splice(i, 1);
7122                     break;
7123                 }
7124             }
7125         }
7126         E.un(el, ename, hd);
7127         el = Roo.getDom(el);
7128         if(ename == "mousewheel" && el.addEventListener){
7129             el.removeEventListener("DOMMouseScroll", hd, false);
7130         }
7131         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7132             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7133         }
7134     };
7135
7136     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7137     
7138     var pub = {
7139         
7140         
7141         /** 
7142          * Fix for doc tools
7143          * @scope Roo.EventManager
7144          */
7145         
7146         
7147         /** 
7148          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7149          * object with a Roo.EventObject
7150          * @param {Function} fn        The method the event invokes
7151          * @param {Object}   scope    An object that becomes the scope of the handler
7152          * @param {boolean}  override If true, the obj passed in becomes
7153          *                             the execution scope of the listener
7154          * @return {Function} The wrapped function
7155          * @deprecated
7156          */
7157         wrap : function(fn, scope, override){
7158             return function(e){
7159                 Roo.EventObject.setEvent(e);
7160                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7161             };
7162         },
7163         
7164         /**
7165      * Appends an event handler to an element (shorthand for addListener)
7166      * @param {String/HTMLElement}   element        The html element or id to assign the
7167      * @param {String}   eventName The type of event to listen for
7168      * @param {Function} handler The method the event invokes
7169      * @param {Object}   scope (optional) The scope in which to execute the handler
7170      * function. The handler function's "this" context.
7171      * @param {Object}   options (optional) An object containing handler configuration
7172      * properties. This may contain any of the following properties:<ul>
7173      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7174      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7175      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7176      * <li>preventDefault {Boolean} True to prevent the default action</li>
7177      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7178      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7179      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7180      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7181      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7182      * by the specified number of milliseconds. If the event fires again within that time, the original
7183      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7184      * </ul><br>
7185      * <p>
7186      * <b>Combining Options</b><br>
7187      * Using the options argument, it is possible to combine different types of listeners:<br>
7188      * <br>
7189      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7190      * Code:<pre><code>
7191 el.on('click', this.onClick, this, {
7192     single: true,
7193     delay: 100,
7194     stopEvent : true,
7195     forumId: 4
7196 });</code></pre>
7197      * <p>
7198      * <b>Attaching multiple handlers in 1 call</b><br>
7199       * The method also allows for a single argument to be passed which is a config object containing properties
7200      * which specify multiple handlers.
7201      * <p>
7202      * Code:<pre><code>
7203 el.on({
7204     'click' : {
7205         fn: this.onClick
7206         scope: this,
7207         delay: 100
7208     },
7209     'mouseover' : {
7210         fn: this.onMouseOver
7211         scope: this
7212     },
7213     'mouseout' : {
7214         fn: this.onMouseOut
7215         scope: this
7216     }
7217 });</code></pre>
7218      * <p>
7219      * Or a shorthand syntax:<br>
7220      * Code:<pre><code>
7221 el.on({
7222     'click' : this.onClick,
7223     'mouseover' : this.onMouseOver,
7224     'mouseout' : this.onMouseOut
7225     scope: this
7226 });</code></pre>
7227      */
7228         addListener : function(element, eventName, fn, scope, options){
7229             if(typeof eventName == "object"){
7230                 var o = eventName;
7231                 for(var e in o){
7232                     if(propRe.test(e)){
7233                         continue;
7234                     }
7235                     if(typeof o[e] == "function"){
7236                         // shared options
7237                         listen(element, e, o, o[e], o.scope);
7238                     }else{
7239                         // individual options
7240                         listen(element, e, o[e]);
7241                     }
7242                 }
7243                 return;
7244             }
7245             return listen(element, eventName, options, fn, scope);
7246         },
7247         
7248         /**
7249          * Removes an event handler
7250          *
7251          * @param {String/HTMLElement}   element        The id or html element to remove the 
7252          *                             event from
7253          * @param {String}   eventName     The type of event
7254          * @param {Function} fn
7255          * @return {Boolean} True if a listener was actually removed
7256          */
7257         removeListener : function(element, eventName, fn){
7258             return stopListening(element, eventName, fn);
7259         },
7260         
7261         /**
7262          * Fires when the document is ready (before onload and before images are loaded). Can be 
7263          * accessed shorthanded Roo.onReady().
7264          * @param {Function} fn        The method the event invokes
7265          * @param {Object}   scope    An  object that becomes the scope of the handler
7266          * @param {boolean}  options
7267          */
7268         onDocumentReady : function(fn, scope, options){
7269             if(docReadyState){ // if it already fired
7270                 docReadyEvent.addListener(fn, scope, options);
7271                 docReadyEvent.fire();
7272                 docReadyEvent.clearListeners();
7273                 return;
7274             }
7275             if(!docReadyEvent){
7276                 initDocReady();
7277             }
7278             docReadyEvent.addListener(fn, scope, options);
7279         },
7280         
7281         /**
7282          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7283          * @param {Function} fn        The method the event invokes
7284          * @param {Object}   scope    An object that becomes the scope of the handler
7285          * @param {boolean}  options
7286          */
7287         onWindowResize : function(fn, scope, options)
7288         {
7289             if(!resizeEvent){
7290                 resizeEvent = new Roo.util.Event();
7291                 resizeTask = new Roo.util.DelayedTask(function(){
7292                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7293                 });
7294                 E.on(window, "resize", function()
7295                 {
7296                     if (Roo.isIE) {
7297                         resizeTask.delay(50);
7298                     } else {
7299                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7300                     }
7301                 });
7302             }
7303             resizeEvent.addListener(fn, scope, options);
7304         },
7305
7306         /**
7307          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7308          * @param {Function} fn        The method the event invokes
7309          * @param {Object}   scope    An object that becomes the scope of the handler
7310          * @param {boolean}  options
7311          */
7312         onTextResize : function(fn, scope, options){
7313             if(!textEvent){
7314                 textEvent = new Roo.util.Event();
7315                 var textEl = new Roo.Element(document.createElement('div'));
7316                 textEl.dom.className = 'x-text-resize';
7317                 textEl.dom.innerHTML = 'X';
7318                 textEl.appendTo(document.body);
7319                 textSize = textEl.dom.offsetHeight;
7320                 setInterval(function(){
7321                     if(textEl.dom.offsetHeight != textSize){
7322                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7323                     }
7324                 }, this.textResizeInterval);
7325             }
7326             textEvent.addListener(fn, scope, options);
7327         },
7328
7329         /**
7330          * Removes the passed window resize listener.
7331          * @param {Function} fn        The method the event invokes
7332          * @param {Object}   scope    The scope of handler
7333          */
7334         removeResizeListener : function(fn, scope){
7335             if(resizeEvent){
7336                 resizeEvent.removeListener(fn, scope);
7337             }
7338         },
7339
7340         // private
7341         fireResize : function(){
7342             if(resizeEvent){
7343                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7344             }   
7345         },
7346         /**
7347          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7348          */
7349         ieDeferSrc : false,
7350         /**
7351          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7352          */
7353         textResizeInterval : 50
7354     };
7355     
7356     /**
7357      * Fix for doc tools
7358      * @scopeAlias pub=Roo.EventManager
7359      */
7360     
7361      /**
7362      * Appends an event handler to an element (shorthand for addListener)
7363      * @param {String/HTMLElement}   element        The html element or id to assign the
7364      * @param {String}   eventName The type of event to listen for
7365      * @param {Function} handler The method the event invokes
7366      * @param {Object}   scope (optional) The scope in which to execute the handler
7367      * function. The handler function's "this" context.
7368      * @param {Object}   options (optional) An object containing handler configuration
7369      * properties. This may contain any of the following properties:<ul>
7370      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7371      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7372      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7373      * <li>preventDefault {Boolean} True to prevent the default action</li>
7374      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7375      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7376      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7377      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7378      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7379      * by the specified number of milliseconds. If the event fires again within that time, the original
7380      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7381      * </ul><br>
7382      * <p>
7383      * <b>Combining Options</b><br>
7384      * Using the options argument, it is possible to combine different types of listeners:<br>
7385      * <br>
7386      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7387      * Code:<pre><code>
7388 el.on('click', this.onClick, this, {
7389     single: true,
7390     delay: 100,
7391     stopEvent : true,
7392     forumId: 4
7393 });</code></pre>
7394      * <p>
7395      * <b>Attaching multiple handlers in 1 call</b><br>
7396       * The method also allows for a single argument to be passed which is a config object containing properties
7397      * which specify multiple handlers.
7398      * <p>
7399      * Code:<pre><code>
7400 el.on({
7401     'click' : {
7402         fn: this.onClick
7403         scope: this,
7404         delay: 100
7405     },
7406     'mouseover' : {
7407         fn: this.onMouseOver
7408         scope: this
7409     },
7410     'mouseout' : {
7411         fn: this.onMouseOut
7412         scope: this
7413     }
7414 });</code></pre>
7415      * <p>
7416      * Or a shorthand syntax:<br>
7417      * Code:<pre><code>
7418 el.on({
7419     'click' : this.onClick,
7420     'mouseover' : this.onMouseOver,
7421     'mouseout' : this.onMouseOut
7422     scope: this
7423 });</code></pre>
7424      */
7425     pub.on = pub.addListener;
7426     pub.un = pub.removeListener;
7427
7428     pub.stoppedMouseDownEvent = new Roo.util.Event();
7429     return pub;
7430 }();
7431 /**
7432   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7433   * @param {Function} fn        The method the event invokes
7434   * @param {Object}   scope    An  object that becomes the scope of the handler
7435   * @param {boolean}  override If true, the obj passed in becomes
7436   *                             the execution scope of the listener
7437   * @member Roo
7438   * @method onReady
7439  */
7440 Roo.onReady = Roo.EventManager.onDocumentReady;
7441
7442 Roo.onReady(function(){
7443     var bd = Roo.get(document.body);
7444     if(!bd){ return; }
7445
7446     var cls = [
7447             Roo.isIE ? "roo-ie"
7448             : Roo.isIE11 ? "roo-ie11"
7449             : Roo.isEdge ? "roo-edge"
7450             : Roo.isGecko ? "roo-gecko"
7451             : Roo.isOpera ? "roo-opera"
7452             : Roo.isSafari ? "roo-safari" : ""];
7453
7454     if(Roo.isMac){
7455         cls.push("roo-mac");
7456     }
7457     if(Roo.isLinux){
7458         cls.push("roo-linux");
7459     }
7460     if(Roo.isIOS){
7461         cls.push("roo-ios");
7462     }
7463     if(Roo.isTouch){
7464         cls.push("roo-touch");
7465     }
7466     if(Roo.isBorderBox){
7467         cls.push('roo-border-box');
7468     }
7469     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7470         var p = bd.dom.parentNode;
7471         if(p){
7472             p.className += ' roo-strict';
7473         }
7474     }
7475     bd.addClass(cls.join(' '));
7476 });
7477
7478 /**
7479  * @class Roo.EventObject
7480  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7481  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7482  * Example:
7483  * <pre><code>
7484  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7485     e.preventDefault();
7486     var target = e.getTarget();
7487     ...
7488  }
7489  var myDiv = Roo.get("myDiv");
7490  myDiv.on("click", handleClick);
7491  //or
7492  Roo.EventManager.on("myDiv", 'click', handleClick);
7493  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7494  </code></pre>
7495  * @static
7496  */
7497 Roo.EventObject = function(){
7498     
7499     var E = Roo.lib.Event;
7500     
7501     // safari keypress events for special keys return bad keycodes
7502     var safariKeys = {
7503         63234 : 37, // left
7504         63235 : 39, // right
7505         63232 : 38, // up
7506         63233 : 40, // down
7507         63276 : 33, // page up
7508         63277 : 34, // page down
7509         63272 : 46, // delete
7510         63273 : 36, // home
7511         63275 : 35  // end
7512     };
7513
7514     // normalize button clicks
7515     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7516                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7517
7518     Roo.EventObjectImpl = function(e){
7519         if(e){
7520             this.setEvent(e.browserEvent || e);
7521         }
7522     };
7523     Roo.EventObjectImpl.prototype = {
7524         /**
7525          * Used to fix doc tools.
7526          * @scope Roo.EventObject.prototype
7527          */
7528             
7529
7530         
7531         
7532         /** The normal browser event */
7533         browserEvent : null,
7534         /** The button pressed in a mouse event */
7535         button : -1,
7536         /** True if the shift key was down during the event */
7537         shiftKey : false,
7538         /** True if the control key was down during the event */
7539         ctrlKey : false,
7540         /** True if the alt key was down during the event */
7541         altKey : false,
7542
7543         /** Key constant 
7544         * @type Number */
7545         BACKSPACE : 8,
7546         /** Key constant 
7547         * @type Number */
7548         TAB : 9,
7549         /** Key constant 
7550         * @type Number */
7551         RETURN : 13,
7552         /** Key constant 
7553         * @type Number */
7554         ENTER : 13,
7555         /** Key constant 
7556         * @type Number */
7557         SHIFT : 16,
7558         /** Key constant 
7559         * @type Number */
7560         CONTROL : 17,
7561         /** Key constant 
7562         * @type Number */
7563         ESC : 27,
7564         /** Key constant 
7565         * @type Number */
7566         SPACE : 32,
7567         /** Key constant 
7568         * @type Number */
7569         PAGEUP : 33,
7570         /** Key constant 
7571         * @type Number */
7572         PAGEDOWN : 34,
7573         /** Key constant 
7574         * @type Number */
7575         END : 35,
7576         /** Key constant 
7577         * @type Number */
7578         HOME : 36,
7579         /** Key constant 
7580         * @type Number */
7581         LEFT : 37,
7582         /** Key constant 
7583         * @type Number */
7584         UP : 38,
7585         /** Key constant 
7586         * @type Number */
7587         RIGHT : 39,
7588         /** Key constant 
7589         * @type Number */
7590         DOWN : 40,
7591         /** Key constant 
7592         * @type Number */
7593         DELETE : 46,
7594         /** Key constant 
7595         * @type Number */
7596         F5 : 116,
7597
7598            /** @private */
7599         setEvent : function(e){
7600             if(e == this || (e && e.browserEvent)){ // already wrapped
7601                 return e;
7602             }
7603             this.browserEvent = e;
7604             if(e){
7605                 // normalize buttons
7606                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7607                 if(e.type == 'click' && this.button == -1){
7608                     this.button = 0;
7609                 }
7610                 this.type = e.type;
7611                 this.shiftKey = e.shiftKey;
7612                 // mac metaKey behaves like ctrlKey
7613                 this.ctrlKey = e.ctrlKey || e.metaKey;
7614                 this.altKey = e.altKey;
7615                 // in getKey these will be normalized for the mac
7616                 this.keyCode = e.keyCode;
7617                 // keyup warnings on firefox.
7618                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7619                 // cache the target for the delayed and or buffered events
7620                 this.target = E.getTarget(e);
7621                 // same for XY
7622                 this.xy = E.getXY(e);
7623             }else{
7624                 this.button = -1;
7625                 this.shiftKey = false;
7626                 this.ctrlKey = false;
7627                 this.altKey = false;
7628                 this.keyCode = 0;
7629                 this.charCode =0;
7630                 this.target = null;
7631                 this.xy = [0, 0];
7632             }
7633             return this;
7634         },
7635
7636         /**
7637          * Stop the event (preventDefault and stopPropagation)
7638          */
7639         stopEvent : function(){
7640             if(this.browserEvent){
7641                 if(this.browserEvent.type == 'mousedown'){
7642                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7643                 }
7644                 E.stopEvent(this.browserEvent);
7645             }
7646         },
7647
7648         /**
7649          * Prevents the browsers default handling of the event.
7650          */
7651         preventDefault : function(){
7652             if(this.browserEvent){
7653                 E.preventDefault(this.browserEvent);
7654             }
7655         },
7656
7657         /** @private */
7658         isNavKeyPress : function(){
7659             var k = this.keyCode;
7660             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7661             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7662         },
7663
7664         isSpecialKey : function(){
7665             var k = this.keyCode;
7666             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7667             (k == 16) || (k == 17) ||
7668             (k >= 18 && k <= 20) ||
7669             (k >= 33 && k <= 35) ||
7670             (k >= 36 && k <= 39) ||
7671             (k >= 44 && k <= 45);
7672         },
7673         /**
7674          * Cancels bubbling of the event.
7675          */
7676         stopPropagation : function(){
7677             if(this.browserEvent){
7678                 if(this.type == 'mousedown'){
7679                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7680                 }
7681                 E.stopPropagation(this.browserEvent);
7682             }
7683         },
7684
7685         /**
7686          * Gets the key code for the event.
7687          * @return {Number}
7688          */
7689         getCharCode : function(){
7690             return this.charCode || this.keyCode;
7691         },
7692
7693         /**
7694          * Returns a normalized keyCode for the event.
7695          * @return {Number} The key code
7696          */
7697         getKey : function(){
7698             var k = this.keyCode || this.charCode;
7699             return Roo.isSafari ? (safariKeys[k] || k) : k;
7700         },
7701
7702         /**
7703          * Gets the x coordinate of the event.
7704          * @return {Number}
7705          */
7706         getPageX : function(){
7707             return this.xy[0];
7708         },
7709
7710         /**
7711          * Gets the y coordinate of the event.
7712          * @return {Number}
7713          */
7714         getPageY : function(){
7715             return this.xy[1];
7716         },
7717
7718         /**
7719          * Gets the time of the event.
7720          * @return {Number}
7721          */
7722         getTime : function(){
7723             if(this.browserEvent){
7724                 return E.getTime(this.browserEvent);
7725             }
7726             return null;
7727         },
7728
7729         /**
7730          * Gets the page coordinates of the event.
7731          * @return {Array} The xy values like [x, y]
7732          */
7733         getXY : function(){
7734             return this.xy;
7735         },
7736
7737         /**
7738          * Gets the target for the event.
7739          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7740          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7741                 search as a number or element (defaults to 10 || document.body)
7742          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7743          * @return {HTMLelement}
7744          */
7745         getTarget : function(selector, maxDepth, returnEl){
7746             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7747         },
7748         /**
7749          * Gets the related target.
7750          * @return {HTMLElement}
7751          */
7752         getRelatedTarget : function(){
7753             if(this.browserEvent){
7754                 return E.getRelatedTarget(this.browserEvent);
7755             }
7756             return null;
7757         },
7758
7759         /**
7760          * Normalizes mouse wheel delta across browsers
7761          * @return {Number} The delta
7762          */
7763         getWheelDelta : function(){
7764             var e = this.browserEvent;
7765             var delta = 0;
7766             if(e.wheelDelta){ /* IE/Opera. */
7767                 delta = e.wheelDelta/120;
7768             }else if(e.detail){ /* Mozilla case. */
7769                 delta = -e.detail/3;
7770             }
7771             return delta;
7772         },
7773
7774         /**
7775          * Returns true if the control, meta, shift or alt key was pressed during this event.
7776          * @return {Boolean}
7777          */
7778         hasModifier : function(){
7779             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7780         },
7781
7782         /**
7783          * Returns true if the target of this event equals el or is a child of el
7784          * @param {String/HTMLElement/Element} el
7785          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7786          * @return {Boolean}
7787          */
7788         within : function(el, related){
7789             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7790             return t && Roo.fly(el).contains(t);
7791         },
7792
7793         getPoint : function(){
7794             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7795         }
7796     };
7797
7798     return new Roo.EventObjectImpl();
7799 }();
7800             
7801     /*
7802  * Based on:
7803  * Ext JS Library 1.1.1
7804  * Copyright(c) 2006-2007, Ext JS, LLC.
7805  *
7806  * Originally Released Under LGPL - original licence link has changed is not relivant.
7807  *
7808  * Fork - LGPL
7809  * <script type="text/javascript">
7810  */
7811
7812  
7813 // was in Composite Element!??!?!
7814  
7815 (function(){
7816     var D = Roo.lib.Dom;
7817     var E = Roo.lib.Event;
7818     var A = Roo.lib.Anim;
7819
7820     // local style camelizing for speed
7821     var propCache = {};
7822     var camelRe = /(-[a-z])/gi;
7823     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7824     var view = document.defaultView;
7825
7826 /**
7827  * @class Roo.Element
7828  * Represents an Element in the DOM.<br><br>
7829  * Usage:<br>
7830 <pre><code>
7831 var el = Roo.get("my-div");
7832
7833 // or with getEl
7834 var el = getEl("my-div");
7835
7836 // or with a DOM element
7837 var el = Roo.get(myDivElement);
7838 </code></pre>
7839  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7840  * each call instead of constructing a new one.<br><br>
7841  * <b>Animations</b><br />
7842  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7843  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7844 <pre>
7845 Option    Default   Description
7846 --------- --------  ---------------------------------------------
7847 duration  .35       The duration of the animation in seconds
7848 easing    easeOut   The YUI easing method
7849 callback  none      A function to execute when the anim completes
7850 scope     this      The scope (this) of the callback function
7851 </pre>
7852 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7853 * manipulate the animation. Here's an example:
7854 <pre><code>
7855 var el = Roo.get("my-div");
7856
7857 // no animation
7858 el.setWidth(100);
7859
7860 // default animation
7861 el.setWidth(100, true);
7862
7863 // animation with some options set
7864 el.setWidth(100, {
7865     duration: 1,
7866     callback: this.foo,
7867     scope: this
7868 });
7869
7870 // using the "anim" property to get the Anim object
7871 var opt = {
7872     duration: 1,
7873     callback: this.foo,
7874     scope: this
7875 };
7876 el.setWidth(100, opt);
7877 ...
7878 if(opt.anim.isAnimated()){
7879     opt.anim.stop();
7880 }
7881 </code></pre>
7882 * <b> Composite (Collections of) Elements</b><br />
7883  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7884  * @constructor Create a new Element directly.
7885  * @param {String/HTMLElement} element
7886  * @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).
7887  */
7888     Roo.Element = function(element, forceNew)
7889     {
7890         var dom = typeof element == "string" ?
7891                 document.getElementById(element) : element;
7892         
7893         this.listeners = {};
7894         
7895         if(!dom){ // invalid id/element
7896             return null;
7897         }
7898         var id = dom.id;
7899         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7900             return Roo.Element.cache[id];
7901         }
7902
7903         /**
7904          * The DOM element
7905          * @type HTMLElement
7906          */
7907         this.dom = dom;
7908
7909         /**
7910          * The DOM element ID
7911          * @type String
7912          */
7913         this.id = id || Roo.id(dom);
7914         
7915         return this; // assumed for cctor?
7916     };
7917
7918     var El = Roo.Element;
7919
7920     El.prototype = {
7921         /**
7922          * The element's default display mode  (defaults to "") 
7923          * @type String
7924          */
7925         originalDisplay : "",
7926
7927         
7928         // note this is overridden in BS version..
7929         visibilityMode : 1, 
7930         /**
7931          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7932          * @type String
7933          */
7934         defaultUnit : "px",
7935         
7936         /**
7937          * Sets the element's visibility mode. When setVisible() is called it
7938          * will use this to determine whether to set the visibility or the display property.
7939          * @param visMode Element.VISIBILITY or Element.DISPLAY
7940          * @return {Roo.Element} this
7941          */
7942         setVisibilityMode : function(visMode){
7943             this.visibilityMode = visMode;
7944             return this;
7945         },
7946         /**
7947          * Convenience method for setVisibilityMode(Element.DISPLAY)
7948          * @param {String} display (optional) What to set display to when visible
7949          * @return {Roo.Element} this
7950          */
7951         enableDisplayMode : function(display){
7952             this.setVisibilityMode(El.DISPLAY);
7953             if(typeof display != "undefined") { this.originalDisplay = display; }
7954             return this;
7955         },
7956
7957         /**
7958          * 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)
7959          * @param {String} selector The simple selector to test
7960          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7961                 search as a number or element (defaults to 10 || document.body)
7962          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7963          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7964          */
7965         findParent : function(simpleSelector, maxDepth, returnEl){
7966             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7967             maxDepth = maxDepth || 50;
7968             if(typeof maxDepth != "number"){
7969                 stopEl = Roo.getDom(maxDepth);
7970                 maxDepth = 10;
7971             }
7972             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7973                 if(dq.is(p, simpleSelector)){
7974                     return returnEl ? Roo.get(p) : p;
7975                 }
7976                 depth++;
7977                 p = p.parentNode;
7978             }
7979             return null;
7980         },
7981
7982
7983         /**
7984          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7985          * @param {String} selector The simple selector to test
7986          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7987                 search as a number or element (defaults to 10 || document.body)
7988          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7989          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7990          */
7991         findParentNode : function(simpleSelector, maxDepth, returnEl){
7992             var p = Roo.fly(this.dom.parentNode, '_internal');
7993             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7994         },
7995         
7996         /**
7997          * Looks at  the scrollable parent element
7998          */
7999         findScrollableParent : function()
8000         {
8001             var overflowRegex = /(auto|scroll)/;
8002             
8003             if(this.getStyle('position') === 'fixed'){
8004                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8005             }
8006             
8007             var excludeStaticParent = this.getStyle('position') === "absolute";
8008             
8009             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8010                 
8011                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8012                     continue;
8013                 }
8014                 
8015                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8016                     return parent;
8017                 }
8018                 
8019                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8020                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8021                 }
8022             }
8023             
8024             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8025         },
8026
8027         /**
8028          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8029          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8030          * @param {String} selector The simple selector to test
8031          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8032                 search as a number or element (defaults to 10 || document.body)
8033          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8034          */
8035         up : function(simpleSelector, maxDepth){
8036             return this.findParentNode(simpleSelector, maxDepth, true);
8037         },
8038
8039
8040
8041         /**
8042          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8043          * @param {String} selector The simple selector to test
8044          * @return {Boolean} True if this element matches the selector, else false
8045          */
8046         is : function(simpleSelector){
8047             return Roo.DomQuery.is(this.dom, simpleSelector);
8048         },
8049
8050         /**
8051          * Perform animation on this element.
8052          * @param {Object} args The YUI animation control args
8053          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8054          * @param {Function} onComplete (optional) Function to call when animation completes
8055          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8056          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8057          * @return {Roo.Element} this
8058          */
8059         animate : function(args, duration, onComplete, easing, animType){
8060             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8061             return this;
8062         },
8063
8064         /*
8065          * @private Internal animation call
8066          */
8067         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8068             animType = animType || 'run';
8069             opt = opt || {};
8070             var anim = Roo.lib.Anim[animType](
8071                 this.dom, args,
8072                 (opt.duration || defaultDur) || .35,
8073                 (opt.easing || defaultEase) || 'easeOut',
8074                 function(){
8075                     Roo.callback(cb, this);
8076                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8077                 },
8078                 this
8079             );
8080             opt.anim = anim;
8081             return anim;
8082         },
8083
8084         // private legacy anim prep
8085         preanim : function(a, i){
8086             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8087         },
8088
8089         /**
8090          * Removes worthless text nodes
8091          * @param {Boolean} forceReclean (optional) By default the element
8092          * keeps track if it has been cleaned already so
8093          * you can call this over and over. However, if you update the element and
8094          * need to force a reclean, you can pass true.
8095          */
8096         clean : function(forceReclean){
8097             if(this.isCleaned && forceReclean !== true){
8098                 return this;
8099             }
8100             var ns = /\S/;
8101             var d = this.dom, n = d.firstChild, ni = -1;
8102             while(n){
8103                 var nx = n.nextSibling;
8104                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8105                     d.removeChild(n);
8106                 }else{
8107                     n.nodeIndex = ++ni;
8108                 }
8109                 n = nx;
8110             }
8111             this.isCleaned = true;
8112             return this;
8113         },
8114
8115         // private
8116         calcOffsetsTo : function(el){
8117             el = Roo.get(el);
8118             var d = el.dom;
8119             var restorePos = false;
8120             if(el.getStyle('position') == 'static'){
8121                 el.position('relative');
8122                 restorePos = true;
8123             }
8124             var x = 0, y =0;
8125             var op = this.dom;
8126             while(op && op != d && op.tagName != 'HTML'){
8127                 x+= op.offsetLeft;
8128                 y+= op.offsetTop;
8129                 op = op.offsetParent;
8130             }
8131             if(restorePos){
8132                 el.position('static');
8133             }
8134             return [x, y];
8135         },
8136
8137         /**
8138          * Scrolls this element into view within the passed container.
8139          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8140          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8141          * @return {Roo.Element} this
8142          */
8143         scrollIntoView : function(container, hscroll){
8144             var c = Roo.getDom(container) || document.body;
8145             var el = this.dom;
8146
8147             var o = this.calcOffsetsTo(c),
8148                 l = o[0],
8149                 t = o[1],
8150                 b = t+el.offsetHeight,
8151                 r = l+el.offsetWidth;
8152
8153             var ch = c.clientHeight;
8154             var ct = parseInt(c.scrollTop, 10);
8155             var cl = parseInt(c.scrollLeft, 10);
8156             var cb = ct + ch;
8157             var cr = cl + c.clientWidth;
8158
8159             if(t < ct){
8160                 c.scrollTop = t;
8161             }else if(b > cb){
8162                 c.scrollTop = b-ch;
8163             }
8164
8165             if(hscroll !== false){
8166                 if(l < cl){
8167                     c.scrollLeft = l;
8168                 }else if(r > cr){
8169                     c.scrollLeft = r-c.clientWidth;
8170                 }
8171             }
8172             return this;
8173         },
8174
8175         // private
8176         scrollChildIntoView : function(child, hscroll){
8177             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8178         },
8179
8180         /**
8181          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8182          * the new height may not be available immediately.
8183          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8184          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8185          * @param {Function} onComplete (optional) Function to call when animation completes
8186          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8187          * @return {Roo.Element} this
8188          */
8189         autoHeight : function(animate, duration, onComplete, easing){
8190             var oldHeight = this.getHeight();
8191             this.clip();
8192             this.setHeight(1); // force clipping
8193             setTimeout(function(){
8194                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8195                 if(!animate){
8196                     this.setHeight(height);
8197                     this.unclip();
8198                     if(typeof onComplete == "function"){
8199                         onComplete();
8200                     }
8201                 }else{
8202                     this.setHeight(oldHeight); // restore original height
8203                     this.setHeight(height, animate, duration, function(){
8204                         this.unclip();
8205                         if(typeof onComplete == "function") { onComplete(); }
8206                     }.createDelegate(this), easing);
8207                 }
8208             }.createDelegate(this), 0);
8209             return this;
8210         },
8211
8212         /**
8213          * Returns true if this element is an ancestor of the passed element
8214          * @param {HTMLElement/String} el The element to check
8215          * @return {Boolean} True if this element is an ancestor of el, else false
8216          */
8217         contains : function(el){
8218             if(!el){return false;}
8219             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8220         },
8221
8222         /**
8223          * Checks whether the element is currently visible using both visibility and display properties.
8224          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8225          * @return {Boolean} True if the element is currently visible, else false
8226          */
8227         isVisible : function(deep) {
8228             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8229             if(deep !== true || !vis){
8230                 return vis;
8231             }
8232             var p = this.dom.parentNode;
8233             while(p && p.tagName.toLowerCase() != "body"){
8234                 if(!Roo.fly(p, '_isVisible').isVisible()){
8235                     return false;
8236                 }
8237                 p = p.parentNode;
8238             }
8239             return true;
8240         },
8241
8242         /**
8243          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8244          * @param {String} selector The CSS selector
8245          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8246          * @return {CompositeElement/CompositeElementLite} The composite element
8247          */
8248         select : function(selector, unique){
8249             return El.select(selector, unique, this.dom);
8250         },
8251
8252         /**
8253          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8254          * @param {String} selector The CSS selector
8255          * @return {Array} An array of the matched nodes
8256          */
8257         query : function(selector, unique){
8258             return Roo.DomQuery.select(selector, this.dom);
8259         },
8260
8261         /**
8262          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8263          * @param {String} selector The CSS selector
8264          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8265          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8266          */
8267         child : function(selector, returnDom){
8268             var n = Roo.DomQuery.selectNode(selector, this.dom);
8269             return returnDom ? n : Roo.get(n);
8270         },
8271
8272         /**
8273          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8274          * @param {String} selector The CSS selector
8275          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8276          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8277          */
8278         down : function(selector, returnDom){
8279             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8280             return returnDom ? n : Roo.get(n);
8281         },
8282
8283         /**
8284          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8285          * @param {String} group The group the DD object is member of
8286          * @param {Object} config The DD config object
8287          * @param {Object} overrides An object containing methods to override/implement on the DD object
8288          * @return {Roo.dd.DD} The DD object
8289          */
8290         initDD : function(group, config, overrides){
8291             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8292             return Roo.apply(dd, overrides);
8293         },
8294
8295         /**
8296          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8297          * @param {String} group The group the DDProxy object is member of
8298          * @param {Object} config The DDProxy config object
8299          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8300          * @return {Roo.dd.DDProxy} The DDProxy object
8301          */
8302         initDDProxy : function(group, config, overrides){
8303             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8304             return Roo.apply(dd, overrides);
8305         },
8306
8307         /**
8308          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8309          * @param {String} group The group the DDTarget object is member of
8310          * @param {Object} config The DDTarget config object
8311          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8312          * @return {Roo.dd.DDTarget} The DDTarget object
8313          */
8314         initDDTarget : function(group, config, overrides){
8315             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8316             return Roo.apply(dd, overrides);
8317         },
8318
8319         /**
8320          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8321          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8322          * @param {Boolean} visible Whether the element is visible
8323          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8324          * @return {Roo.Element} this
8325          */
8326          setVisible : function(visible, animate){
8327             if(!animate || !A){
8328                 if(this.visibilityMode == El.DISPLAY){
8329                     this.setDisplayed(visible);
8330                 }else{
8331                     this.fixDisplay();
8332                     this.dom.style.visibility = visible ? "visible" : "hidden";
8333                 }
8334             }else{
8335                 // closure for composites
8336                 var dom = this.dom;
8337                 var visMode = this.visibilityMode;
8338                 if(visible){
8339                     this.setOpacity(.01);
8340                     this.setVisible(true);
8341                 }
8342                 this.anim({opacity: { to: (visible?1:0) }},
8343                       this.preanim(arguments, 1),
8344                       null, .35, 'easeIn', function(){
8345                          if(!visible){
8346                              if(visMode == El.DISPLAY){
8347                                  dom.style.display = "none";
8348                              }else{
8349                                  dom.style.visibility = "hidden";
8350                              }
8351                              Roo.get(dom).setOpacity(1);
8352                          }
8353                      });
8354             }
8355             return this;
8356         },
8357
8358         /**
8359          * Returns true if display is not "none"
8360          * @return {Boolean}
8361          */
8362         isDisplayed : function() {
8363             return this.getStyle("display") != "none";
8364         },
8365
8366         /**
8367          * Toggles the element's visibility or display, depending on visibility mode.
8368          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8369          * @return {Roo.Element} this
8370          */
8371         toggle : function(animate){
8372             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8373             return this;
8374         },
8375
8376         /**
8377          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8378          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8379          * @return {Roo.Element} this
8380          */
8381         setDisplayed : function(value) {
8382             if(typeof value == "boolean"){
8383                value = value ? this.originalDisplay : "none";
8384             }
8385             this.setStyle("display", value);
8386             return this;
8387         },
8388
8389         /**
8390          * Tries to focus the element. Any exceptions are caught and ignored.
8391          * @return {Roo.Element} this
8392          */
8393         focus : function() {
8394             try{
8395                 this.dom.focus();
8396             }catch(e){}
8397             return this;
8398         },
8399
8400         /**
8401          * Tries to blur the element. Any exceptions are caught and ignored.
8402          * @return {Roo.Element} this
8403          */
8404         blur : function() {
8405             try{
8406                 this.dom.blur();
8407             }catch(e){}
8408             return this;
8409         },
8410
8411         /**
8412          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8413          * @param {String/Array} className The CSS class to add, or an array of classes
8414          * @return {Roo.Element} this
8415          */
8416         addClass : function(className){
8417             if(className instanceof Array){
8418                 for(var i = 0, len = className.length; i < len; i++) {
8419                     this.addClass(className[i]);
8420                 }
8421             }else{
8422                 if(className && !this.hasClass(className)){
8423                     if (this.dom instanceof SVGElement) {
8424                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8425                     } else {
8426                         this.dom.className = this.dom.className + " " + className;
8427                     }
8428                 }
8429             }
8430             return this;
8431         },
8432
8433         /**
8434          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8435          * @param {String/Array} className The CSS class to add, or an array of classes
8436          * @return {Roo.Element} this
8437          */
8438         radioClass : function(className){
8439             var siblings = this.dom.parentNode.childNodes;
8440             for(var i = 0; i < siblings.length; i++) {
8441                 var s = siblings[i];
8442                 if(s.nodeType == 1){
8443                     Roo.get(s).removeClass(className);
8444                 }
8445             }
8446             this.addClass(className);
8447             return this;
8448         },
8449
8450         /**
8451          * Removes one or more CSS classes from the element.
8452          * @param {String/Array} className The CSS class to remove, or an array of classes
8453          * @return {Roo.Element} this
8454          */
8455         removeClass : function(className){
8456             
8457             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8458             if(!className || !cn){
8459                 return this;
8460             }
8461             if(className instanceof Array){
8462                 for(var i = 0, len = className.length; i < len; i++) {
8463                     this.removeClass(className[i]);
8464                 }
8465             }else{
8466                 if(this.hasClass(className)){
8467                     var re = this.classReCache[className];
8468                     if (!re) {
8469                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8470                        this.classReCache[className] = re;
8471                     }
8472                     if (this.dom instanceof SVGElement) {
8473                         this.dom.className.baseVal = cn.replace(re, " ");
8474                     } else {
8475                         this.dom.className = cn.replace(re, " ");
8476                     }
8477                 }
8478             }
8479             return this;
8480         },
8481
8482         // private
8483         classReCache: {},
8484
8485         /**
8486          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8487          * @param {String} className The CSS class to toggle
8488          * @return {Roo.Element} this
8489          */
8490         toggleClass : function(className){
8491             if(this.hasClass(className)){
8492                 this.removeClass(className);
8493             }else{
8494                 this.addClass(className);
8495             }
8496             return this;
8497         },
8498
8499         /**
8500          * Checks if the specified CSS class exists on this element's DOM node.
8501          * @param {String} className The CSS class to check for
8502          * @return {Boolean} True if the class exists, else false
8503          */
8504         hasClass : function(className){
8505             if (this.dom instanceof SVGElement) {
8506                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8507             } 
8508             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8509         },
8510
8511         /**
8512          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8513          * @param {String} oldClassName The CSS class to replace
8514          * @param {String} newClassName The replacement CSS class
8515          * @return {Roo.Element} this
8516          */
8517         replaceClass : function(oldClassName, newClassName){
8518             this.removeClass(oldClassName);
8519             this.addClass(newClassName);
8520             return this;
8521         },
8522
8523         /**
8524          * Returns an object with properties matching the styles requested.
8525          * For example, el.getStyles('color', 'font-size', 'width') might return
8526          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8527          * @param {String} style1 A style name
8528          * @param {String} style2 A style name
8529          * @param {String} etc.
8530          * @return {Object} The style object
8531          */
8532         getStyles : function(){
8533             var a = arguments, len = a.length, r = {};
8534             for(var i = 0; i < len; i++){
8535                 r[a[i]] = this.getStyle(a[i]);
8536             }
8537             return r;
8538         },
8539
8540         /**
8541          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8542          * @param {String} property The style property whose value is returned.
8543          * @return {String} The current value of the style property for this element.
8544          */
8545         getStyle : function(){
8546             return view && view.getComputedStyle ?
8547                 function(prop){
8548                     var el = this.dom, v, cs, camel;
8549                     if(prop == 'float'){
8550                         prop = "cssFloat";
8551                     }
8552                     if(el.style && (v = el.style[prop])){
8553                         return v;
8554                     }
8555                     if(cs = view.getComputedStyle(el, "")){
8556                         if(!(camel = propCache[prop])){
8557                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8558                         }
8559                         return cs[camel];
8560                     }
8561                     return null;
8562                 } :
8563                 function(prop){
8564                     var el = this.dom, v, cs, camel;
8565                     if(prop == 'opacity'){
8566                         if(typeof el.style.filter == 'string'){
8567                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8568                             if(m){
8569                                 var fv = parseFloat(m[1]);
8570                                 if(!isNaN(fv)){
8571                                     return fv ? fv / 100 : 0;
8572                                 }
8573                             }
8574                         }
8575                         return 1;
8576                     }else if(prop == 'float'){
8577                         prop = "styleFloat";
8578                     }
8579                     if(!(camel = propCache[prop])){
8580                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8581                     }
8582                     if(v = el.style[camel]){
8583                         return v;
8584                     }
8585                     if(cs = el.currentStyle){
8586                         return cs[camel];
8587                     }
8588                     return null;
8589                 };
8590         }(),
8591
8592         /**
8593          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8594          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8595          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8596          * @return {Roo.Element} this
8597          */
8598         setStyle : function(prop, value){
8599             if(typeof prop == "string"){
8600                 
8601                 if (prop == 'float') {
8602                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8603                     return this;
8604                 }
8605                 
8606                 var camel;
8607                 if(!(camel = propCache[prop])){
8608                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8609                 }
8610                 
8611                 if(camel == 'opacity') {
8612                     this.setOpacity(value);
8613                 }else{
8614                     this.dom.style[camel] = value;
8615                 }
8616             }else{
8617                 for(var style in prop){
8618                     if(typeof prop[style] != "function"){
8619                        this.setStyle(style, prop[style]);
8620                     }
8621                 }
8622             }
8623             return this;
8624         },
8625
8626         /**
8627          * More flexible version of {@link #setStyle} for setting style properties.
8628          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8629          * a function which returns such a specification.
8630          * @return {Roo.Element} this
8631          */
8632         applyStyles : function(style){
8633             Roo.DomHelper.applyStyles(this.dom, style);
8634             return this;
8635         },
8636
8637         /**
8638           * 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).
8639           * @return {Number} The X position of the element
8640           */
8641         getX : function(){
8642             return D.getX(this.dom);
8643         },
8644
8645         /**
8646           * 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).
8647           * @return {Number} The Y position of the element
8648           */
8649         getY : function(){
8650             return D.getY(this.dom);
8651         },
8652
8653         /**
8654           * 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).
8655           * @return {Array} The XY position of the element
8656           */
8657         getXY : function(){
8658             return D.getXY(this.dom);
8659         },
8660
8661         /**
8662          * 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).
8663          * @param {Number} The X position of the element
8664          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8665          * @return {Roo.Element} this
8666          */
8667         setX : function(x, animate){
8668             if(!animate || !A){
8669                 D.setX(this.dom, x);
8670             }else{
8671                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8672             }
8673             return this;
8674         },
8675
8676         /**
8677          * 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).
8678          * @param {Number} The Y position of the element
8679          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8680          * @return {Roo.Element} this
8681          */
8682         setY : function(y, animate){
8683             if(!animate || !A){
8684                 D.setY(this.dom, y);
8685             }else{
8686                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8687             }
8688             return this;
8689         },
8690
8691         /**
8692          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8693          * @param {String} left The left CSS property value
8694          * @return {Roo.Element} this
8695          */
8696         setLeft : function(left){
8697             this.setStyle("left", this.addUnits(left));
8698             return this;
8699         },
8700
8701         /**
8702          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8703          * @param {String} top The top CSS property value
8704          * @return {Roo.Element} this
8705          */
8706         setTop : function(top){
8707             this.setStyle("top", this.addUnits(top));
8708             return this;
8709         },
8710
8711         /**
8712          * Sets the element's CSS right style.
8713          * @param {String} right The right CSS property value
8714          * @return {Roo.Element} this
8715          */
8716         setRight : function(right){
8717             this.setStyle("right", this.addUnits(right));
8718             return this;
8719         },
8720
8721         /**
8722          * Sets the element's CSS bottom style.
8723          * @param {String} bottom The bottom CSS property value
8724          * @return {Roo.Element} this
8725          */
8726         setBottom : function(bottom){
8727             this.setStyle("bottom", this.addUnits(bottom));
8728             return this;
8729         },
8730
8731         /**
8732          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8733          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8734          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8735          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8736          * @return {Roo.Element} this
8737          */
8738         setXY : function(pos, animate){
8739             if(!animate || !A){
8740                 D.setXY(this.dom, pos);
8741             }else{
8742                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8743             }
8744             return this;
8745         },
8746
8747         /**
8748          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8749          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8750          * @param {Number} x X value for new position (coordinates are page-based)
8751          * @param {Number} y Y value for new position (coordinates are page-based)
8752          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8753          * @return {Roo.Element} this
8754          */
8755         setLocation : function(x, y, animate){
8756             this.setXY([x, y], this.preanim(arguments, 2));
8757             return this;
8758         },
8759
8760         /**
8761          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8762          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8763          * @param {Number} x X value for new position (coordinates are page-based)
8764          * @param {Number} y Y value for new position (coordinates are page-based)
8765          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8766          * @return {Roo.Element} this
8767          */
8768         moveTo : function(x, y, animate){
8769             this.setXY([x, y], this.preanim(arguments, 2));
8770             return this;
8771         },
8772
8773         /**
8774          * Returns the region of the given element.
8775          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8776          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8777          */
8778         getRegion : function(){
8779             return D.getRegion(this.dom);
8780         },
8781
8782         /**
8783          * Returns the offset height of the element
8784          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8785          * @return {Number} The element's height
8786          */
8787         getHeight : function(contentHeight){
8788             var h = this.dom.offsetHeight || 0;
8789             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8790         },
8791
8792         /**
8793          * Returns the offset width of the element
8794          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8795          * @return {Number} The element's width
8796          */
8797         getWidth : function(contentWidth){
8798             var w = this.dom.offsetWidth || 0;
8799             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8800         },
8801
8802         /**
8803          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8804          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8805          * if a height has not been set using CSS.
8806          * @return {Number}
8807          */
8808         getComputedHeight : function(){
8809             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8810             if(!h){
8811                 h = parseInt(this.getStyle('height'), 10) || 0;
8812                 if(!this.isBorderBox()){
8813                     h += this.getFrameWidth('tb');
8814                 }
8815             }
8816             return h;
8817         },
8818
8819         /**
8820          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8821          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8822          * if a width has not been set using CSS.
8823          * @return {Number}
8824          */
8825         getComputedWidth : function(){
8826             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8827             if(!w){
8828                 w = parseInt(this.getStyle('width'), 10) || 0;
8829                 if(!this.isBorderBox()){
8830                     w += this.getFrameWidth('lr');
8831                 }
8832             }
8833             return w;
8834         },
8835
8836         /**
8837          * Returns the size of the element.
8838          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8839          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8840          */
8841         getSize : function(contentSize){
8842             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8843         },
8844
8845         /**
8846          * Returns the width and height of the viewport.
8847          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8848          */
8849         getViewSize : function(){
8850             var d = this.dom, doc = document, aw = 0, ah = 0;
8851             if(d == doc || d == doc.body){
8852                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8853             }else{
8854                 return {
8855                     width : d.clientWidth,
8856                     height: d.clientHeight
8857                 };
8858             }
8859         },
8860
8861         /**
8862          * Returns the value of the "value" attribute
8863          * @param {Boolean} asNumber true to parse the value as a number
8864          * @return {String/Number}
8865          */
8866         getValue : function(asNumber){
8867             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8868         },
8869
8870         // private
8871         adjustWidth : function(width){
8872             if(typeof width == "number"){
8873                 if(this.autoBoxAdjust && !this.isBorderBox()){
8874                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8875                 }
8876                 if(width < 0){
8877                     width = 0;
8878                 }
8879             }
8880             return width;
8881         },
8882
8883         // private
8884         adjustHeight : function(height){
8885             if(typeof height == "number"){
8886                if(this.autoBoxAdjust && !this.isBorderBox()){
8887                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8888                }
8889                if(height < 0){
8890                    height = 0;
8891                }
8892             }
8893             return height;
8894         },
8895
8896         /**
8897          * Set the width of the element
8898          * @param {Number} width The new width
8899          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8900          * @return {Roo.Element} this
8901          */
8902         setWidth : function(width, animate){
8903             width = this.adjustWidth(width);
8904             if(!animate || !A){
8905                 this.dom.style.width = this.addUnits(width);
8906             }else{
8907                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8908             }
8909             return this;
8910         },
8911
8912         /**
8913          * Set the height of the element
8914          * @param {Number} height The new height
8915          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8916          * @return {Roo.Element} this
8917          */
8918          setHeight : function(height, animate){
8919             height = this.adjustHeight(height);
8920             if(!animate || !A){
8921                 this.dom.style.height = this.addUnits(height);
8922             }else{
8923                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8924             }
8925             return this;
8926         },
8927
8928         /**
8929          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8930          * @param {Number} width The new width
8931          * @param {Number} height The new height
8932          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8933          * @return {Roo.Element} this
8934          */
8935          setSize : function(width, height, animate){
8936             if(typeof width == "object"){ // in case of object from getSize()
8937                 height = width.height; width = width.width;
8938             }
8939             width = this.adjustWidth(width); height = this.adjustHeight(height);
8940             if(!animate || !A){
8941                 this.dom.style.width = this.addUnits(width);
8942                 this.dom.style.height = this.addUnits(height);
8943             }else{
8944                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8945             }
8946             return this;
8947         },
8948
8949         /**
8950          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8951          * @param {Number} x X value for new position (coordinates are page-based)
8952          * @param {Number} y Y value for new position (coordinates are page-based)
8953          * @param {Number} width The new width
8954          * @param {Number} height The new height
8955          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8956          * @return {Roo.Element} this
8957          */
8958         setBounds : function(x, y, width, height, animate){
8959             if(!animate || !A){
8960                 this.setSize(width, height);
8961                 this.setLocation(x, y);
8962             }else{
8963                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8964                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8965                               this.preanim(arguments, 4), 'motion');
8966             }
8967             return this;
8968         },
8969
8970         /**
8971          * 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.
8972          * @param {Roo.lib.Region} region The region to fill
8973          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8974          * @return {Roo.Element} this
8975          */
8976         setRegion : function(region, animate){
8977             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8978             return this;
8979         },
8980
8981         /**
8982          * Appends an event handler
8983          *
8984          * @param {String}   eventName     The type of event to append
8985          * @param {Function} fn        The method the event invokes
8986          * @param {Object} scope       (optional) The scope (this object) of the fn
8987          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8988          */
8989         addListener : function(eventName, fn, scope, options)
8990         {
8991             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8992                 this.addListener('touchstart', this.onTapHandler, this);
8993             }
8994             
8995             // we need to handle a special case where dom element is a svg element.
8996             // in this case we do not actua
8997             if (!this.dom) {
8998                 return;
8999             }
9000             
9001             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9002                 if (typeof(this.listeners[eventName]) == 'undefined') {
9003                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9004                 }
9005                 this.listeners[eventName].addListener(fn, scope, options);
9006                 return;
9007             }
9008             
9009                 
9010             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9011             
9012             
9013         },
9014         tapedTwice : false,
9015         onTapHandler : function(event)
9016         {
9017             if(!this.tapedTwice) {
9018                 this.tapedTwice = true;
9019                 var s = this;
9020                 setTimeout( function() {
9021                     s.tapedTwice = false;
9022                 }, 300 );
9023                 return;
9024             }
9025             event.preventDefault();
9026             var revent = new MouseEvent('dblclick',  {
9027                 view: window,
9028                 bubbles: true,
9029                 cancelable: true
9030             });
9031              
9032             this.dom.dispatchEvent(revent);
9033             //action on double tap goes below
9034              
9035         }, 
9036  
9037         /**
9038          * Removes an event handler from this element
9039          * @param {String} eventName the type of event to remove
9040          * @param {Function} fn the method the event invokes
9041          * @param {Function} scope (needed for svg fake listeners)
9042          * @return {Roo.Element} this
9043          */
9044         removeListener : function(eventName, fn, scope){
9045             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9046             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9047                 return this;
9048             }
9049             this.listeners[eventName].removeListener(fn, scope);
9050             return this;
9051         },
9052
9053         /**
9054          * Removes all previous added listeners from this element
9055          * @return {Roo.Element} this
9056          */
9057         removeAllListeners : function(){
9058             E.purgeElement(this.dom);
9059             this.listeners = {};
9060             return this;
9061         },
9062
9063         relayEvent : function(eventName, observable){
9064             this.on(eventName, function(e){
9065                 observable.fireEvent(eventName, e);
9066             });
9067         },
9068
9069         
9070         /**
9071          * Set the opacity of the element
9072          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9073          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9074          * @return {Roo.Element} this
9075          */
9076          setOpacity : function(opacity, animate){
9077             if(!animate || !A){
9078                 var s = this.dom.style;
9079                 if(Roo.isIE){
9080                     s.zoom = 1;
9081                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9082                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9083                 }else{
9084                     s.opacity = opacity;
9085                 }
9086             }else{
9087                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9088             }
9089             return this;
9090         },
9091
9092         /**
9093          * Gets the left X coordinate
9094          * @param {Boolean} local True to get the local css position instead of page coordinate
9095          * @return {Number}
9096          */
9097         getLeft : function(local){
9098             if(!local){
9099                 return this.getX();
9100             }else{
9101                 return parseInt(this.getStyle("left"), 10) || 0;
9102             }
9103         },
9104
9105         /**
9106          * Gets the right X coordinate of the element (element X position + element width)
9107          * @param {Boolean} local True to get the local css position instead of page coordinate
9108          * @return {Number}
9109          */
9110         getRight : function(local){
9111             if(!local){
9112                 return this.getX() + this.getWidth();
9113             }else{
9114                 return (this.getLeft(true) + this.getWidth()) || 0;
9115             }
9116         },
9117
9118         /**
9119          * Gets the top Y coordinate
9120          * @param {Boolean} local True to get the local css position instead of page coordinate
9121          * @return {Number}
9122          */
9123         getTop : function(local) {
9124             if(!local){
9125                 return this.getY();
9126             }else{
9127                 return parseInt(this.getStyle("top"), 10) || 0;
9128             }
9129         },
9130
9131         /**
9132          * Gets the bottom Y coordinate of the element (element Y position + element height)
9133          * @param {Boolean} local True to get the local css position instead of page coordinate
9134          * @return {Number}
9135          */
9136         getBottom : function(local){
9137             if(!local){
9138                 return this.getY() + this.getHeight();
9139             }else{
9140                 return (this.getTop(true) + this.getHeight()) || 0;
9141             }
9142         },
9143
9144         /**
9145         * Initializes positioning on this element. If a desired position is not passed, it will make the
9146         * the element positioned relative IF it is not already positioned.
9147         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9148         * @param {Number} zIndex (optional) The zIndex to apply
9149         * @param {Number} x (optional) Set the page X position
9150         * @param {Number} y (optional) Set the page Y position
9151         */
9152         position : function(pos, zIndex, x, y){
9153             if(!pos){
9154                if(this.getStyle('position') == 'static'){
9155                    this.setStyle('position', 'relative');
9156                }
9157             }else{
9158                 this.setStyle("position", pos);
9159             }
9160             if(zIndex){
9161                 this.setStyle("z-index", zIndex);
9162             }
9163             if(x !== undefined && y !== undefined){
9164                 this.setXY([x, y]);
9165             }else if(x !== undefined){
9166                 this.setX(x);
9167             }else if(y !== undefined){
9168                 this.setY(y);
9169             }
9170         },
9171
9172         /**
9173         * Clear positioning back to the default when the document was loaded
9174         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9175         * @return {Roo.Element} this
9176          */
9177         clearPositioning : function(value){
9178             value = value ||'';
9179             this.setStyle({
9180                 "left": value,
9181                 "right": value,
9182                 "top": value,
9183                 "bottom": value,
9184                 "z-index": "",
9185                 "position" : "static"
9186             });
9187             return this;
9188         },
9189
9190         /**
9191         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9192         * snapshot before performing an update and then restoring the element.
9193         * @return {Object}
9194         */
9195         getPositioning : function(){
9196             var l = this.getStyle("left");
9197             var t = this.getStyle("top");
9198             return {
9199                 "position" : this.getStyle("position"),
9200                 "left" : l,
9201                 "right" : l ? "" : this.getStyle("right"),
9202                 "top" : t,
9203                 "bottom" : t ? "" : this.getStyle("bottom"),
9204                 "z-index" : this.getStyle("z-index")
9205             };
9206         },
9207
9208         /**
9209          * Gets the width of the border(s) for the specified side(s)
9210          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9211          * passing lr would get the border (l)eft width + the border (r)ight width.
9212          * @return {Number} The width of the sides passed added together
9213          */
9214         getBorderWidth : function(side){
9215             return this.addStyles(side, El.borders);
9216         },
9217
9218         /**
9219          * Gets the width of the padding(s) for the specified side(s)
9220          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9221          * passing lr would get the padding (l)eft + the padding (r)ight.
9222          * @return {Number} The padding of the sides passed added together
9223          */
9224         getPadding : function(side){
9225             return this.addStyles(side, El.paddings);
9226         },
9227
9228         /**
9229         * Set positioning with an object returned by getPositioning().
9230         * @param {Object} posCfg
9231         * @return {Roo.Element} this
9232          */
9233         setPositioning : function(pc){
9234             this.applyStyles(pc);
9235             if(pc.right == "auto"){
9236                 this.dom.style.right = "";
9237             }
9238             if(pc.bottom == "auto"){
9239                 this.dom.style.bottom = "";
9240             }
9241             return this;
9242         },
9243
9244         // private
9245         fixDisplay : function(){
9246             if(this.getStyle("display") == "none"){
9247                 this.setStyle("visibility", "hidden");
9248                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9249                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9250                     this.setStyle("display", "block");
9251                 }
9252             }
9253         },
9254
9255         /**
9256          * Quick set left and top adding default units
9257          * @param {String} left The left CSS property value
9258          * @param {String} top The top CSS property value
9259          * @return {Roo.Element} this
9260          */
9261          setLeftTop : function(left, top){
9262             this.dom.style.left = this.addUnits(left);
9263             this.dom.style.top = this.addUnits(top);
9264             return this;
9265         },
9266
9267         /**
9268          * Move this element relative to its current position.
9269          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9270          * @param {Number} distance How far to move the element in pixels
9271          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9272          * @return {Roo.Element} this
9273          */
9274          move : function(direction, distance, animate){
9275             var xy = this.getXY();
9276             direction = direction.toLowerCase();
9277             switch(direction){
9278                 case "l":
9279                 case "left":
9280                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9281                     break;
9282                case "r":
9283                case "right":
9284                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9285                     break;
9286                case "t":
9287                case "top":
9288                case "up":
9289                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9290                     break;
9291                case "b":
9292                case "bottom":
9293                case "down":
9294                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9295                     break;
9296             }
9297             return this;
9298         },
9299
9300         /**
9301          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9302          * @return {Roo.Element} this
9303          */
9304         clip : function(){
9305             if(!this.isClipped){
9306                this.isClipped = true;
9307                this.originalClip = {
9308                    "o": this.getStyle("overflow"),
9309                    "x": this.getStyle("overflow-x"),
9310                    "y": this.getStyle("overflow-y")
9311                };
9312                this.setStyle("overflow", "hidden");
9313                this.setStyle("overflow-x", "hidden");
9314                this.setStyle("overflow-y", "hidden");
9315             }
9316             return this;
9317         },
9318
9319         /**
9320          *  Return clipping (overflow) to original clipping before clip() was called
9321          * @return {Roo.Element} this
9322          */
9323         unclip : function(){
9324             if(this.isClipped){
9325                 this.isClipped = false;
9326                 var o = this.originalClip;
9327                 if(o.o){this.setStyle("overflow", o.o);}
9328                 if(o.x){this.setStyle("overflow-x", o.x);}
9329                 if(o.y){this.setStyle("overflow-y", o.y);}
9330             }
9331             return this;
9332         },
9333
9334
9335         /**
9336          * Gets the x,y coordinates specified by the anchor position on the element.
9337          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9338          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9339          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9340          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9341          * @return {Array} [x, y] An array containing the element's x and y coordinates
9342          */
9343         getAnchorXY : function(anchor, local, s){
9344             //Passing a different size is useful for pre-calculating anchors,
9345             //especially for anchored animations that change the el size.
9346
9347             var w, h, vp = false;
9348             if(!s){
9349                 var d = this.dom;
9350                 if(d == document.body || d == document){
9351                     vp = true;
9352                     w = D.getViewWidth(); h = D.getViewHeight();
9353                 }else{
9354                     w = this.getWidth(); h = this.getHeight();
9355                 }
9356             }else{
9357                 w = s.width;  h = s.height;
9358             }
9359             var x = 0, y = 0, r = Math.round;
9360             switch((anchor || "tl").toLowerCase()){
9361                 case "c":
9362                     x = r(w*.5);
9363                     y = r(h*.5);
9364                 break;
9365                 case "t":
9366                     x = r(w*.5);
9367                     y = 0;
9368                 break;
9369                 case "l":
9370                     x = 0;
9371                     y = r(h*.5);
9372                 break;
9373                 case "r":
9374                     x = w;
9375                     y = r(h*.5);
9376                 break;
9377                 case "b":
9378                     x = r(w*.5);
9379                     y = h;
9380                 break;
9381                 case "tl":
9382                     x = 0;
9383                     y = 0;
9384                 break;
9385                 case "bl":
9386                     x = 0;
9387                     y = h;
9388                 break;
9389                 case "br":
9390                     x = w;
9391                     y = h;
9392                 break;
9393                 case "tr":
9394                     x = w;
9395                     y = 0;
9396                 break;
9397             }
9398             if(local === true){
9399                 return [x, y];
9400             }
9401             if(vp){
9402                 var sc = this.getScroll();
9403                 return [x + sc.left, y + sc.top];
9404             }
9405             //Add the element's offset xy
9406             var o = this.getXY();
9407             return [x+o[0], y+o[1]];
9408         },
9409
9410         /**
9411          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9412          * supported position values.
9413          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9414          * @param {String} position The position to align to.
9415          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9416          * @return {Array} [x, y]
9417          */
9418         getAlignToXY : function(el, p, o)
9419         {
9420             el = Roo.get(el);
9421             var d = this.dom;
9422             if(!el.dom){
9423                 throw "Element.alignTo with an element that doesn't exist";
9424             }
9425             var c = false; //constrain to viewport
9426             var p1 = "", p2 = "";
9427             o = o || [0,0];
9428
9429             if(!p){
9430                 p = "tl-bl";
9431             }else if(p == "?"){
9432                 p = "tl-bl?";
9433             }else if(p.indexOf("-") == -1){
9434                 p = "tl-" + p;
9435             }
9436             p = p.toLowerCase();
9437             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9438             if(!m){
9439                throw "Element.alignTo with an invalid alignment " + p;
9440             }
9441             p1 = m[1]; p2 = m[2]; c = !!m[3];
9442
9443             //Subtract the aligned el's internal xy from the target's offset xy
9444             //plus custom offset to get the aligned el's new offset xy
9445             var a1 = this.getAnchorXY(p1, true);
9446             var a2 = el.getAnchorXY(p2, false);
9447             var x = a2[0] - a1[0] + o[0];
9448             var y = a2[1] - a1[1] + o[1];
9449             if(c){
9450                 //constrain the aligned el to viewport if necessary
9451                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9452                 // 5px of margin for ie
9453                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9454
9455                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9456                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9457                 //otherwise swap the aligned el to the opposite border of the target.
9458                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9459                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9460                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9461                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9462
9463                var doc = document;
9464                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9465                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9466
9467                if((x+w) > dw + scrollX){
9468                     x = swapX ? r.left-w : dw+scrollX-w;
9469                 }
9470                if(x < scrollX){
9471                    x = swapX ? r.right : scrollX;
9472                }
9473                if((y+h) > dh + scrollY){
9474                     y = swapY ? r.top-h : dh+scrollY-h;
9475                 }
9476                if (y < scrollY){
9477                    y = swapY ? r.bottom : scrollY;
9478                }
9479             }
9480             return [x,y];
9481         },
9482
9483         // private
9484         getConstrainToXY : function(){
9485             var os = {top:0, left:0, bottom:0, right: 0};
9486
9487             return function(el, local, offsets, proposedXY){
9488                 el = Roo.get(el);
9489                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9490
9491                 var vw, vh, vx = 0, vy = 0;
9492                 if(el.dom == document.body || el.dom == document){
9493                     vw = Roo.lib.Dom.getViewWidth();
9494                     vh = Roo.lib.Dom.getViewHeight();
9495                 }else{
9496                     vw = el.dom.clientWidth;
9497                     vh = el.dom.clientHeight;
9498                     if(!local){
9499                         var vxy = el.getXY();
9500                         vx = vxy[0];
9501                         vy = vxy[1];
9502                     }
9503                 }
9504
9505                 var s = el.getScroll();
9506
9507                 vx += offsets.left + s.left;
9508                 vy += offsets.top + s.top;
9509
9510                 vw -= offsets.right;
9511                 vh -= offsets.bottom;
9512
9513                 var vr = vx+vw;
9514                 var vb = vy+vh;
9515
9516                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9517                 var x = xy[0], y = xy[1];
9518                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9519
9520                 // only move it if it needs it
9521                 var moved = false;
9522
9523                 // first validate right/bottom
9524                 if((x + w) > vr){
9525                     x = vr - w;
9526                     moved = true;
9527                 }
9528                 if((y + h) > vb){
9529                     y = vb - h;
9530                     moved = true;
9531                 }
9532                 // then make sure top/left isn't negative
9533                 if(x < vx){
9534                     x = vx;
9535                     moved = true;
9536                 }
9537                 if(y < vy){
9538                     y = vy;
9539                     moved = true;
9540                 }
9541                 return moved ? [x, y] : false;
9542             };
9543         }(),
9544
9545         // private
9546         adjustForConstraints : function(xy, parent, offsets){
9547             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9548         },
9549
9550         /**
9551          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9552          * document it aligns it to the viewport.
9553          * The position parameter is optional, and can be specified in any one of the following formats:
9554          * <ul>
9555          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9556          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9557          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9558          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9559          *   <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
9560          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9561          * </ul>
9562          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9563          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9564          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9565          * that specified in order to enforce the viewport constraints.
9566          * Following are all of the supported anchor positions:
9567     <pre>
9568     Value  Description
9569     -----  -----------------------------
9570     tl     The top left corner (default)
9571     t      The center of the top edge
9572     tr     The top right corner
9573     l      The center of the left edge
9574     c      In the center of the element
9575     r      The center of the right edge
9576     bl     The bottom left corner
9577     b      The center of the bottom edge
9578     br     The bottom right corner
9579     </pre>
9580     Example Usage:
9581     <pre><code>
9582     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9583     el.alignTo("other-el");
9584
9585     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9586     el.alignTo("other-el", "tr?");
9587
9588     // align the bottom right corner of el with the center left edge of other-el
9589     el.alignTo("other-el", "br-l?");
9590
9591     // align the center of el with the bottom left corner of other-el and
9592     // adjust the x position by -6 pixels (and the y position by 0)
9593     el.alignTo("other-el", "c-bl", [-6, 0]);
9594     </code></pre>
9595          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9596          * @param {String} position The position to align to.
9597          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9598          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9599          * @return {Roo.Element} this
9600          */
9601         alignTo : function(element, position, offsets, animate){
9602             var xy = this.getAlignToXY(element, position, offsets);
9603             this.setXY(xy, this.preanim(arguments, 3));
9604             return this;
9605         },
9606
9607         /**
9608          * Anchors an element to another element and realigns it when the window is resized.
9609          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9610          * @param {String} position The position to align to.
9611          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9612          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9613          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9614          * is a number, it is used as the buffer delay (defaults to 50ms).
9615          * @param {Function} callback The function to call after the animation finishes
9616          * @return {Roo.Element} this
9617          */
9618         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9619             var action = function(){
9620                 this.alignTo(el, alignment, offsets, animate);
9621                 Roo.callback(callback, this);
9622             };
9623             Roo.EventManager.onWindowResize(action, this);
9624             var tm = typeof monitorScroll;
9625             if(tm != 'undefined'){
9626                 Roo.EventManager.on(window, 'scroll', action, this,
9627                     {buffer: tm == 'number' ? monitorScroll : 50});
9628             }
9629             action.call(this); // align immediately
9630             return this;
9631         },
9632         /**
9633          * Clears any opacity settings from this element. Required in some cases for IE.
9634          * @return {Roo.Element} this
9635          */
9636         clearOpacity : function(){
9637             if (window.ActiveXObject) {
9638                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9639                     this.dom.style.filter = "";
9640                 }
9641             } else {
9642                 this.dom.style.opacity = "";
9643                 this.dom.style["-moz-opacity"] = "";
9644                 this.dom.style["-khtml-opacity"] = "";
9645             }
9646             return this;
9647         },
9648
9649         /**
9650          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9651          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9652          * @return {Roo.Element} this
9653          */
9654         hide : function(animate){
9655             this.setVisible(false, this.preanim(arguments, 0));
9656             return this;
9657         },
9658
9659         /**
9660         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9661         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9662          * @return {Roo.Element} this
9663          */
9664         show : function(animate){
9665             this.setVisible(true, this.preanim(arguments, 0));
9666             return this;
9667         },
9668
9669         /**
9670          * @private Test if size has a unit, otherwise appends the default
9671          */
9672         addUnits : function(size){
9673             return Roo.Element.addUnits(size, this.defaultUnit);
9674         },
9675
9676         /**
9677          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9678          * @return {Roo.Element} this
9679          */
9680         beginMeasure : function(){
9681             var el = this.dom;
9682             if(el.offsetWidth || el.offsetHeight){
9683                 return this; // offsets work already
9684             }
9685             var changed = [];
9686             var p = this.dom, b = document.body; // start with this element
9687             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9688                 var pe = Roo.get(p);
9689                 if(pe.getStyle('display') == 'none'){
9690                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9691                     p.style.visibility = "hidden";
9692                     p.style.display = "block";
9693                 }
9694                 p = p.parentNode;
9695             }
9696             this._measureChanged = changed;
9697             return this;
9698
9699         },
9700
9701         /**
9702          * Restores displays to before beginMeasure was called
9703          * @return {Roo.Element} this
9704          */
9705         endMeasure : function(){
9706             var changed = this._measureChanged;
9707             if(changed){
9708                 for(var i = 0, len = changed.length; i < len; i++) {
9709                     var r = changed[i];
9710                     r.el.style.visibility = r.visibility;
9711                     r.el.style.display = "none";
9712                 }
9713                 this._measureChanged = null;
9714             }
9715             return this;
9716         },
9717
9718         /**
9719         * Update the innerHTML of this element, optionally searching for and processing scripts
9720         * @param {String} html The new HTML
9721         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9722         * @param {Function} callback For async script loading you can be noticed when the update completes
9723         * @return {Roo.Element} this
9724          */
9725         update : function(html, loadScripts, callback){
9726             if(typeof html == "undefined"){
9727                 html = "";
9728             }
9729             if(loadScripts !== true){
9730                 this.dom.innerHTML = html;
9731                 if(typeof callback == "function"){
9732                     callback();
9733                 }
9734                 return this;
9735             }
9736             var id = Roo.id();
9737             var dom = this.dom;
9738
9739             html += '<span id="' + id + '"></span>';
9740
9741             E.onAvailable(id, function(){
9742                 var hd = document.getElementsByTagName("head")[0];
9743                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9744                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9745                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9746
9747                 var match;
9748                 while(match = re.exec(html)){
9749                     var attrs = match[1];
9750                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9751                     if(srcMatch && srcMatch[2]){
9752                        var s = document.createElement("script");
9753                        s.src = srcMatch[2];
9754                        var typeMatch = attrs.match(typeRe);
9755                        if(typeMatch && typeMatch[2]){
9756                            s.type = typeMatch[2];
9757                        }
9758                        hd.appendChild(s);
9759                     }else if(match[2] && match[2].length > 0){
9760                         if(window.execScript) {
9761                            window.execScript(match[2]);
9762                         } else {
9763                             /**
9764                              * eval:var:id
9765                              * eval:var:dom
9766                              * eval:var:html
9767                              * 
9768                              */
9769                            window.eval(match[2]);
9770                         }
9771                     }
9772                 }
9773                 var el = document.getElementById(id);
9774                 if(el){el.parentNode.removeChild(el);}
9775                 if(typeof callback == "function"){
9776                     callback();
9777                 }
9778             });
9779             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9780             return this;
9781         },
9782
9783         /**
9784          * Direct access to the UpdateManager update() method (takes the same parameters).
9785          * @param {String/Function} url The url for this request or a function to call to get the url
9786          * @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}
9787          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9788          * @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.
9789          * @return {Roo.Element} this
9790          */
9791         load : function(){
9792             var um = this.getUpdateManager();
9793             um.update.apply(um, arguments);
9794             return this;
9795         },
9796
9797         /**
9798         * Gets this element's UpdateManager
9799         * @return {Roo.UpdateManager} The UpdateManager
9800         */
9801         getUpdateManager : function(){
9802             if(!this.updateManager){
9803                 this.updateManager = new Roo.UpdateManager(this);
9804             }
9805             return this.updateManager;
9806         },
9807
9808         /**
9809          * Disables text selection for this element (normalized across browsers)
9810          * @return {Roo.Element} this
9811          */
9812         unselectable : function(){
9813             this.dom.unselectable = "on";
9814             this.swallowEvent("selectstart", true);
9815             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9816             this.addClass("x-unselectable");
9817             return this;
9818         },
9819
9820         /**
9821         * Calculates the x, y to center this element on the screen
9822         * @return {Array} The x, y values [x, y]
9823         */
9824         getCenterXY : function(){
9825             return this.getAlignToXY(document, 'c-c');
9826         },
9827
9828         /**
9829         * Centers the Element in either the viewport, or another Element.
9830         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9831         */
9832         center : function(centerIn){
9833             this.alignTo(centerIn || document, 'c-c');
9834             return this;
9835         },
9836
9837         /**
9838          * Tests various css rules/browsers to determine if this element uses a border box
9839          * @return {Boolean}
9840          */
9841         isBorderBox : function(){
9842             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9843         },
9844
9845         /**
9846          * Return a box {x, y, width, height} that can be used to set another elements
9847          * size/location to match this element.
9848          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9849          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9850          * @return {Object} box An object in the format {x, y, width, height}
9851          */
9852         getBox : function(contentBox, local){
9853             var xy;
9854             if(!local){
9855                 xy = this.getXY();
9856             }else{
9857                 var left = parseInt(this.getStyle("left"), 10) || 0;
9858                 var top = parseInt(this.getStyle("top"), 10) || 0;
9859                 xy = [left, top];
9860             }
9861             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9862             if(!contentBox){
9863                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9864             }else{
9865                 var l = this.getBorderWidth("l")+this.getPadding("l");
9866                 var r = this.getBorderWidth("r")+this.getPadding("r");
9867                 var t = this.getBorderWidth("t")+this.getPadding("t");
9868                 var b = this.getBorderWidth("b")+this.getPadding("b");
9869                 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)};
9870             }
9871             bx.right = bx.x + bx.width;
9872             bx.bottom = bx.y + bx.height;
9873             return bx;
9874         },
9875
9876         /**
9877          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9878          for more information about the sides.
9879          * @param {String} sides
9880          * @return {Number}
9881          */
9882         getFrameWidth : function(sides, onlyContentBox){
9883             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9884         },
9885
9886         /**
9887          * 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.
9888          * @param {Object} box The box to fill {x, y, width, height}
9889          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9890          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9891          * @return {Roo.Element} this
9892          */
9893         setBox : function(box, adjust, animate){
9894             var w = box.width, h = box.height;
9895             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9896                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9897                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9898             }
9899             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9900             return this;
9901         },
9902
9903         /**
9904          * Forces the browser to repaint this element
9905          * @return {Roo.Element} this
9906          */
9907          repaint : function(){
9908             var dom = this.dom;
9909             this.addClass("x-repaint");
9910             setTimeout(function(){
9911                 Roo.get(dom).removeClass("x-repaint");
9912             }, 1);
9913             return this;
9914         },
9915
9916         /**
9917          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9918          * then it returns the calculated width of the sides (see getPadding)
9919          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9920          * @return {Object/Number}
9921          */
9922         getMargins : function(side){
9923             if(!side){
9924                 return {
9925                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9926                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9927                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9928                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9929                 };
9930             }else{
9931                 return this.addStyles(side, El.margins);
9932              }
9933         },
9934
9935         // private
9936         addStyles : function(sides, styles){
9937             var val = 0, v, w;
9938             for(var i = 0, len = sides.length; i < len; i++){
9939                 v = this.getStyle(styles[sides.charAt(i)]);
9940                 if(v){
9941                      w = parseInt(v, 10);
9942                      if(w){ val += w; }
9943                 }
9944             }
9945             return val;
9946         },
9947
9948         /**
9949          * Creates a proxy element of this element
9950          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9951          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9952          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9953          * @return {Roo.Element} The new proxy element
9954          */
9955         createProxy : function(config, renderTo, matchBox){
9956             if(renderTo){
9957                 renderTo = Roo.getDom(renderTo);
9958             }else{
9959                 renderTo = document.body;
9960             }
9961             config = typeof config == "object" ?
9962                 config : {tag : "div", cls: config};
9963             var proxy = Roo.DomHelper.append(renderTo, config, true);
9964             if(matchBox){
9965                proxy.setBox(this.getBox());
9966             }
9967             return proxy;
9968         },
9969
9970         /**
9971          * Puts a mask over this element to disable user interaction. Requires core.css.
9972          * This method can only be applied to elements which accept child nodes.
9973          * @param {String} msg (optional) A message to display in the mask
9974          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
9975          * @return {Element} The mask  element
9976          */
9977         mask : function(msg, msgCls)
9978         {
9979             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9980                 this.setStyle("position", "relative");
9981             }
9982             if(!this._mask){
9983                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9984             }
9985             
9986             this.addClass("x-masked");
9987             this._mask.setDisplayed(true);
9988             
9989             // we wander
9990             var z = 0;
9991             var dom = this.dom;
9992             while (dom && dom.style) {
9993                 if (!isNaN(parseInt(dom.style.zIndex))) {
9994                     z = Math.max(z, parseInt(dom.style.zIndex));
9995                 }
9996                 dom = dom.parentNode;
9997             }
9998             // if we are masking the body - then it hides everything..
9999             if (this.dom == document.body) {
10000                 z = 1000000;
10001                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10002                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10003             }
10004            
10005             if(typeof msg == 'string'){
10006                 if(!this._maskMsg){
10007                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10008                         cls: "roo-el-mask-msg", 
10009                         cn: [
10010                             {
10011                                 tag: 'i',
10012                                 cls: 'fa fa-spinner fa-spin'
10013                             },
10014                             {
10015                                 tag: 'div'
10016                             }   
10017                         ]
10018                     }, true);
10019                 }
10020                 var mm = this._maskMsg;
10021                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10022                 if (mm.dom.lastChild) { // weird IE issue?
10023                     mm.dom.lastChild.innerHTML = msg;
10024                 }
10025                 mm.setDisplayed(true);
10026                 mm.center(this);
10027                 mm.setStyle('z-index', z + 102);
10028             }
10029             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10030                 this._mask.setHeight(this.getHeight());
10031             }
10032             this._mask.setStyle('z-index', z + 100);
10033             
10034             return this._mask;
10035         },
10036
10037         /**
10038          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10039          * it is cached for reuse.
10040          */
10041         unmask : function(removeEl){
10042             if(this._mask){
10043                 if(removeEl === true){
10044                     this._mask.remove();
10045                     delete this._mask;
10046                     if(this._maskMsg){
10047                         this._maskMsg.remove();
10048                         delete this._maskMsg;
10049                     }
10050                 }else{
10051                     this._mask.setDisplayed(false);
10052                     if(this._maskMsg){
10053                         this._maskMsg.setDisplayed(false);
10054                     }
10055                 }
10056             }
10057             this.removeClass("x-masked");
10058         },
10059
10060         /**
10061          * Returns true if this element is masked
10062          * @return {Boolean}
10063          */
10064         isMasked : function(){
10065             return this._mask && this._mask.isVisible();
10066         },
10067
10068         /**
10069          * Creates an iframe shim for this element to keep selects and other windowed objects from
10070          * showing through.
10071          * @return {Roo.Element} The new shim element
10072          */
10073         createShim : function(){
10074             var el = document.createElement('iframe');
10075             el.frameBorder = 'no';
10076             el.className = 'roo-shim';
10077             if(Roo.isIE && Roo.isSecure){
10078                 el.src = Roo.SSL_SECURE_URL;
10079             }
10080             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10081             shim.autoBoxAdjust = false;
10082             return shim;
10083         },
10084
10085         /**
10086          * Removes this element from the DOM and deletes it from the cache
10087          */
10088         remove : function(){
10089             if(this.dom.parentNode){
10090                 this.dom.parentNode.removeChild(this.dom);
10091             }
10092             delete El.cache[this.dom.id];
10093         },
10094
10095         /**
10096          * Sets up event handlers to add and remove a css class when the mouse is over this element
10097          * @param {String} className
10098          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10099          * mouseout events for children elements
10100          * @return {Roo.Element} this
10101          */
10102         addClassOnOver : function(className, preventFlicker){
10103             this.on("mouseover", function(){
10104                 Roo.fly(this, '_internal').addClass(className);
10105             }, this.dom);
10106             var removeFn = function(e){
10107                 if(preventFlicker !== true || !e.within(this, true)){
10108                     Roo.fly(this, '_internal').removeClass(className);
10109                 }
10110             };
10111             this.on("mouseout", removeFn, this.dom);
10112             return this;
10113         },
10114
10115         /**
10116          * Sets up event handlers to add and remove a css class when this element has the focus
10117          * @param {String} className
10118          * @return {Roo.Element} this
10119          */
10120         addClassOnFocus : function(className){
10121             this.on("focus", function(){
10122                 Roo.fly(this, '_internal').addClass(className);
10123             }, this.dom);
10124             this.on("blur", function(){
10125                 Roo.fly(this, '_internal').removeClass(className);
10126             }, this.dom);
10127             return this;
10128         },
10129         /**
10130          * 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)
10131          * @param {String} className
10132          * @return {Roo.Element} this
10133          */
10134         addClassOnClick : function(className){
10135             var dom = this.dom;
10136             this.on("mousedown", function(){
10137                 Roo.fly(dom, '_internal').addClass(className);
10138                 var d = Roo.get(document);
10139                 var fn = function(){
10140                     Roo.fly(dom, '_internal').removeClass(className);
10141                     d.removeListener("mouseup", fn);
10142                 };
10143                 d.on("mouseup", fn);
10144             });
10145             return this;
10146         },
10147
10148         /**
10149          * Stops the specified event from bubbling and optionally prevents the default action
10150          * @param {String} eventName
10151          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10152          * @return {Roo.Element} this
10153          */
10154         swallowEvent : function(eventName, preventDefault){
10155             var fn = function(e){
10156                 e.stopPropagation();
10157                 if(preventDefault){
10158                     e.preventDefault();
10159                 }
10160             };
10161             if(eventName instanceof Array){
10162                 for(var i = 0, len = eventName.length; i < len; i++){
10163                      this.on(eventName[i], fn);
10164                 }
10165                 return this;
10166             }
10167             this.on(eventName, fn);
10168             return this;
10169         },
10170
10171         /**
10172          * @private
10173          */
10174         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10175
10176         /**
10177          * Sizes this element to its parent element's dimensions performing
10178          * neccessary box adjustments.
10179          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10180          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10181          * @return {Roo.Element} this
10182          */
10183         fitToParent : function(monitorResize, targetParent) {
10184           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10185           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10186           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10187             return this;
10188           }
10189           var p = Roo.get(targetParent || this.dom.parentNode);
10190           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10191           if (monitorResize === true) {
10192             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10193             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10194           }
10195           return this;
10196         },
10197
10198         /**
10199          * Gets the next sibling, skipping text nodes
10200          * @return {HTMLElement} The next sibling or null
10201          */
10202         getNextSibling : function(){
10203             var n = this.dom.nextSibling;
10204             while(n && n.nodeType != 1){
10205                 n = n.nextSibling;
10206             }
10207             return n;
10208         },
10209
10210         /**
10211          * Gets the previous sibling, skipping text nodes
10212          * @return {HTMLElement} The previous sibling or null
10213          */
10214         getPrevSibling : function(){
10215             var n = this.dom.previousSibling;
10216             while(n && n.nodeType != 1){
10217                 n = n.previousSibling;
10218             }
10219             return n;
10220         },
10221
10222
10223         /**
10224          * Appends the passed element(s) to this element
10225          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10226          * @return {Roo.Element} this
10227          */
10228         appendChild: function(el){
10229             el = Roo.get(el);
10230             el.appendTo(this);
10231             return this;
10232         },
10233
10234         /**
10235          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10236          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10237          * automatically generated with the specified attributes.
10238          * @param {HTMLElement} insertBefore (optional) a child element of this element
10239          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10240          * @return {Roo.Element} The new child element
10241          */
10242         createChild: function(config, insertBefore, returnDom){
10243             config = config || {tag:'div'};
10244             if(insertBefore){
10245                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10246             }
10247             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10248         },
10249
10250         /**
10251          * Appends this element to the passed element
10252          * @param {String/HTMLElement/Element} el The new parent element
10253          * @return {Roo.Element} this
10254          */
10255         appendTo: function(el){
10256             el = Roo.getDom(el);
10257             el.appendChild(this.dom);
10258             return this;
10259         },
10260
10261         /**
10262          * Inserts this element before the passed element in the DOM
10263          * @param {String/HTMLElement/Element} el The element to insert before
10264          * @return {Roo.Element} this
10265          */
10266         insertBefore: function(el){
10267             el = Roo.getDom(el);
10268             el.parentNode.insertBefore(this.dom, el);
10269             return this;
10270         },
10271
10272         /**
10273          * Inserts this element after the passed element in the DOM
10274          * @param {String/HTMLElement/Element} el The element to insert after
10275          * @return {Roo.Element} this
10276          */
10277         insertAfter: function(el){
10278             el = Roo.getDom(el);
10279             el.parentNode.insertBefore(this.dom, el.nextSibling);
10280             return this;
10281         },
10282
10283         /**
10284          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10285          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10286          * @return {Roo.Element} The new child
10287          */
10288         insertFirst: function(el, returnDom){
10289             el = el || {};
10290             if(typeof el == 'object' && !el.nodeType){ // dh config
10291                 return this.createChild(el, this.dom.firstChild, returnDom);
10292             }else{
10293                 el = Roo.getDom(el);
10294                 this.dom.insertBefore(el, this.dom.firstChild);
10295                 return !returnDom ? Roo.get(el) : el;
10296             }
10297         },
10298
10299         /**
10300          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10301          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10302          * @param {String} where (optional) 'before' or 'after' defaults to before
10303          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10304          * @return {Roo.Element} the inserted Element
10305          */
10306         insertSibling: function(el, where, returnDom){
10307             where = where ? where.toLowerCase() : 'before';
10308             el = el || {};
10309             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10310
10311             if(typeof el == 'object' && !el.nodeType){ // dh config
10312                 if(where == 'after' && !this.dom.nextSibling){
10313                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10314                 }else{
10315                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10316                 }
10317
10318             }else{
10319                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10320                             where == 'before' ? this.dom : this.dom.nextSibling);
10321                 if(!returnDom){
10322                     rt = Roo.get(rt);
10323                 }
10324             }
10325             return rt;
10326         },
10327
10328         /**
10329          * Creates and wraps this element with another element
10330          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10331          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10332          * @return {HTMLElement/Element} The newly created wrapper element
10333          */
10334         wrap: function(config, returnDom){
10335             if(!config){
10336                 config = {tag: "div"};
10337             }
10338             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10339             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10340             return newEl;
10341         },
10342
10343         /**
10344          * Replaces the passed element with this element
10345          * @param {String/HTMLElement/Element} el The element to replace
10346          * @return {Roo.Element} this
10347          */
10348         replace: function(el){
10349             el = Roo.get(el);
10350             this.insertBefore(el);
10351             el.remove();
10352             return this;
10353         },
10354
10355         /**
10356          * Inserts an html fragment into this element
10357          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10358          * @param {String} html The HTML fragment
10359          * @param {Boolean} returnEl True to return an Roo.Element
10360          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10361          */
10362         insertHtml : function(where, html, returnEl){
10363             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10364             return returnEl ? Roo.get(el) : el;
10365         },
10366
10367         /**
10368          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10369          * @param {Object} o The object with the attributes
10370          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10371          * @return {Roo.Element} this
10372          */
10373         set : function(o, useSet){
10374             var el = this.dom;
10375             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10376             for(var attr in o){
10377                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10378                 if(attr=="cls"){
10379                     el.className = o["cls"];
10380                 }else{
10381                     if(useSet) {
10382                         el.setAttribute(attr, o[attr]);
10383                     } else {
10384                         el[attr] = o[attr];
10385                     }
10386                 }
10387             }
10388             if(o.style){
10389                 Roo.DomHelper.applyStyles(el, o.style);
10390             }
10391             return this;
10392         },
10393
10394         /**
10395          * Convenience method for constructing a KeyMap
10396          * @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:
10397          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10398          * @param {Function} fn The function to call
10399          * @param {Object} scope (optional) The scope of the function
10400          * @return {Roo.KeyMap} The KeyMap created
10401          */
10402         addKeyListener : function(key, fn, scope){
10403             var config;
10404             if(typeof key != "object" || key instanceof Array){
10405                 config = {
10406                     key: key,
10407                     fn: fn,
10408                     scope: scope
10409                 };
10410             }else{
10411                 config = {
10412                     key : key.key,
10413                     shift : key.shift,
10414                     ctrl : key.ctrl,
10415                     alt : key.alt,
10416                     fn: fn,
10417                     scope: scope
10418                 };
10419             }
10420             return new Roo.KeyMap(this, config);
10421         },
10422
10423         /**
10424          * Creates a KeyMap for this element
10425          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10426          * @return {Roo.KeyMap} The KeyMap created
10427          */
10428         addKeyMap : function(config){
10429             return new Roo.KeyMap(this, config);
10430         },
10431
10432         /**
10433          * Returns true if this element is scrollable.
10434          * @return {Boolean}
10435          */
10436          isScrollable : function(){
10437             var dom = this.dom;
10438             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10439         },
10440
10441         /**
10442          * 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().
10443          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10444          * @param {Number} value The new scroll value
10445          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10446          * @return {Element} this
10447          */
10448
10449         scrollTo : function(side, value, animate){
10450             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10451             if(!animate || !A){
10452                 this.dom[prop] = value;
10453             }else{
10454                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10455                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10456             }
10457             return this;
10458         },
10459
10460         /**
10461          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10462          * within this element's scrollable range.
10463          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10464          * @param {Number} distance How far to scroll the element in pixels
10465          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10466          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10467          * was scrolled as far as it could go.
10468          */
10469          scroll : function(direction, distance, animate){
10470              if(!this.isScrollable()){
10471                  return;
10472              }
10473              var el = this.dom;
10474              var l = el.scrollLeft, t = el.scrollTop;
10475              var w = el.scrollWidth, h = el.scrollHeight;
10476              var cw = el.clientWidth, ch = el.clientHeight;
10477              direction = direction.toLowerCase();
10478              var scrolled = false;
10479              var a = this.preanim(arguments, 2);
10480              switch(direction){
10481                  case "l":
10482                  case "left":
10483                      if(w - l > cw){
10484                          var v = Math.min(l + distance, w-cw);
10485                          this.scrollTo("left", v, a);
10486                          scrolled = true;
10487                      }
10488                      break;
10489                 case "r":
10490                 case "right":
10491                      if(l > 0){
10492                          var v = Math.max(l - distance, 0);
10493                          this.scrollTo("left", v, a);
10494                          scrolled = true;
10495                      }
10496                      break;
10497                 case "t":
10498                 case "top":
10499                 case "up":
10500                      if(t > 0){
10501                          var v = Math.max(t - distance, 0);
10502                          this.scrollTo("top", v, a);
10503                          scrolled = true;
10504                      }
10505                      break;
10506                 case "b":
10507                 case "bottom":
10508                 case "down":
10509                      if(h - t > ch){
10510                          var v = Math.min(t + distance, h-ch);
10511                          this.scrollTo("top", v, a);
10512                          scrolled = true;
10513                      }
10514                      break;
10515              }
10516              return scrolled;
10517         },
10518
10519         /**
10520          * Translates the passed page coordinates into left/top css values for this element
10521          * @param {Number/Array} x The page x or an array containing [x, y]
10522          * @param {Number} y The page y
10523          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10524          */
10525         translatePoints : function(x, y){
10526             if(typeof x == 'object' || x instanceof Array){
10527                 y = x[1]; x = x[0];
10528             }
10529             var p = this.getStyle('position');
10530             var o = this.getXY();
10531
10532             var l = parseInt(this.getStyle('left'), 10);
10533             var t = parseInt(this.getStyle('top'), 10);
10534
10535             if(isNaN(l)){
10536                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10537             }
10538             if(isNaN(t)){
10539                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10540             }
10541
10542             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10543         },
10544
10545         /**
10546          * Returns the current scroll position of the element.
10547          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10548          */
10549         getScroll : function(){
10550             var d = this.dom, doc = document;
10551             if(d == doc || d == doc.body){
10552                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10553                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10554                 return {left: l, top: t};
10555             }else{
10556                 return {left: d.scrollLeft, top: d.scrollTop};
10557             }
10558         },
10559
10560         /**
10561          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10562          * are convert to standard 6 digit hex color.
10563          * @param {String} attr The css attribute
10564          * @param {String} defaultValue The default value to use when a valid color isn't found
10565          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10566          * YUI color anims.
10567          */
10568         getColor : function(attr, defaultValue, prefix){
10569             var v = this.getStyle(attr);
10570             if(!v || v == "transparent" || v == "inherit") {
10571                 return defaultValue;
10572             }
10573             var color = typeof prefix == "undefined" ? "#" : prefix;
10574             if(v.substr(0, 4) == "rgb("){
10575                 var rvs = v.slice(4, v.length -1).split(",");
10576                 for(var i = 0; i < 3; i++){
10577                     var h = parseInt(rvs[i]).toString(16);
10578                     if(h < 16){
10579                         h = "0" + h;
10580                     }
10581                     color += h;
10582                 }
10583             } else {
10584                 if(v.substr(0, 1) == "#"){
10585                     if(v.length == 4) {
10586                         for(var i = 1; i < 4; i++){
10587                             var c = v.charAt(i);
10588                             color +=  c + c;
10589                         }
10590                     }else if(v.length == 7){
10591                         color += v.substr(1);
10592                     }
10593                 }
10594             }
10595             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10596         },
10597
10598         /**
10599          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10600          * gradient background, rounded corners and a 4-way shadow.
10601          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10602          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10603          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10604          * @return {Roo.Element} this
10605          */
10606         boxWrap : function(cls){
10607             cls = cls || 'x-box';
10608             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10609             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10610             return el;
10611         },
10612
10613         /**
10614          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10615          * @param {String} namespace The namespace in which to look for the attribute
10616          * @param {String} name The attribute name
10617          * @return {String} The attribute value
10618          */
10619         getAttributeNS : Roo.isIE ? function(ns, name){
10620             var d = this.dom;
10621             var type = typeof d[ns+":"+name];
10622             if(type != 'undefined' && type != 'unknown'){
10623                 return d[ns+":"+name];
10624             }
10625             return d[name];
10626         } : function(ns, name){
10627             var d = this.dom;
10628             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10629         },
10630         
10631         
10632         /**
10633          * Sets or Returns the value the dom attribute value
10634          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10635          * @param {String} value (optional) The value to set the attribute to
10636          * @return {String} The attribute value
10637          */
10638         attr : function(name){
10639             if (arguments.length > 1) {
10640                 this.dom.setAttribute(name, arguments[1]);
10641                 return arguments[1];
10642             }
10643             if (typeof(name) == 'object') {
10644                 for(var i in name) {
10645                     this.attr(i, name[i]);
10646                 }
10647                 return name;
10648             }
10649             
10650             
10651             if (!this.dom.hasAttribute(name)) {
10652                 return undefined;
10653             }
10654             return this.dom.getAttribute(name);
10655         }
10656         
10657         
10658         
10659     };
10660
10661     var ep = El.prototype;
10662
10663     /**
10664      * Appends an event handler (Shorthand for addListener)
10665      * @param {String}   eventName     The type of event to append
10666      * @param {Function} fn        The method the event invokes
10667      * @param {Object} scope       (optional) The scope (this object) of the fn
10668      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10669      * @method
10670      */
10671     ep.on = ep.addListener;
10672         // backwards compat
10673     ep.mon = ep.addListener;
10674
10675     /**
10676      * Removes an event handler from this element (shorthand for removeListener)
10677      * @param {String} eventName the type of event to remove
10678      * @param {Function} fn the method the event invokes
10679      * @return {Roo.Element} this
10680      * @method
10681      */
10682     ep.un = ep.removeListener;
10683
10684     /**
10685      * true to automatically adjust width and height settings for box-model issues (default to true)
10686      */
10687     ep.autoBoxAdjust = true;
10688
10689     // private
10690     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10691
10692     // private
10693     El.addUnits = function(v, defaultUnit){
10694         if(v === "" || v == "auto"){
10695             return v;
10696         }
10697         if(v === undefined){
10698             return '';
10699         }
10700         if(typeof v == "number" || !El.unitPattern.test(v)){
10701             return v + (defaultUnit || 'px');
10702         }
10703         return v;
10704     };
10705
10706     // special markup used throughout Roo when box wrapping elements
10707     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>';
10708     /**
10709      * Visibility mode constant - Use visibility to hide element
10710      * @static
10711      * @type Number
10712      */
10713     El.VISIBILITY = 1;
10714     /**
10715      * Visibility mode constant - Use display to hide element
10716      * @static
10717      * @type Number
10718      */
10719     El.DISPLAY = 2;
10720
10721     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10722     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10723     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10724
10725
10726
10727     /**
10728      * @private
10729      */
10730     El.cache = {};
10731
10732     var docEl;
10733
10734     /**
10735      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10736      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10737      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10738      * @return {Element} The Element object
10739      * @static
10740      */
10741     El.get = function(el){
10742         var ex, elm, id;
10743         if(!el){ return null; }
10744         if(typeof el == "string"){ // element id
10745             if(!(elm = document.getElementById(el))){
10746                 return null;
10747             }
10748             if(ex = El.cache[el]){
10749                 ex.dom = elm;
10750             }else{
10751                 ex = El.cache[el] = new El(elm);
10752             }
10753             return ex;
10754         }else if(el.tagName){ // dom element
10755             if(!(id = el.id)){
10756                 id = Roo.id(el);
10757             }
10758             if(ex = El.cache[id]){
10759                 ex.dom = el;
10760             }else{
10761                 ex = El.cache[id] = new El(el);
10762             }
10763             return ex;
10764         }else if(el instanceof El){
10765             if(el != docEl){
10766                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10767                                                               // catch case where it hasn't been appended
10768                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10769             }
10770             return el;
10771         }else if(el.isComposite){
10772             return el;
10773         }else if(el instanceof Array){
10774             return El.select(el);
10775         }else if(el == document){
10776             // create a bogus element object representing the document object
10777             if(!docEl){
10778                 var f = function(){};
10779                 f.prototype = El.prototype;
10780                 docEl = new f();
10781                 docEl.dom = document;
10782             }
10783             return docEl;
10784         }
10785         return null;
10786     };
10787
10788     // private
10789     El.uncache = function(el){
10790         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10791             if(a[i]){
10792                 delete El.cache[a[i].id || a[i]];
10793             }
10794         }
10795     };
10796
10797     // private
10798     // Garbage collection - uncache elements/purge listeners on orphaned elements
10799     // so we don't hold a reference and cause the browser to retain them
10800     El.garbageCollect = function(){
10801         if(!Roo.enableGarbageCollector){
10802             clearInterval(El.collectorThread);
10803             return;
10804         }
10805         for(var eid in El.cache){
10806             var el = El.cache[eid], d = el.dom;
10807             // -------------------------------------------------------
10808             // Determining what is garbage:
10809             // -------------------------------------------------------
10810             // !d
10811             // dom node is null, definitely garbage
10812             // -------------------------------------------------------
10813             // !d.parentNode
10814             // no parentNode == direct orphan, definitely garbage
10815             // -------------------------------------------------------
10816             // !d.offsetParent && !document.getElementById(eid)
10817             // display none elements have no offsetParent so we will
10818             // also try to look it up by it's id. However, check
10819             // offsetParent first so we don't do unneeded lookups.
10820             // This enables collection of elements that are not orphans
10821             // directly, but somewhere up the line they have an orphan
10822             // parent.
10823             // -------------------------------------------------------
10824             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10825                 delete El.cache[eid];
10826                 if(d && Roo.enableListenerCollection){
10827                     E.purgeElement(d);
10828                 }
10829             }
10830         }
10831     }
10832     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10833
10834
10835     // dom is optional
10836     El.Flyweight = function(dom){
10837         this.dom = dom;
10838     };
10839     El.Flyweight.prototype = El.prototype;
10840
10841     El._flyweights = {};
10842     /**
10843      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10844      * the dom node can be overwritten by other code.
10845      * @param {String/HTMLElement} el The dom node or id
10846      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10847      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10848      * @static
10849      * @return {Element} The shared Element object
10850      */
10851     El.fly = function(el, named){
10852         named = named || '_global';
10853         el = Roo.getDom(el);
10854         if(!el){
10855             return null;
10856         }
10857         if(!El._flyweights[named]){
10858             El._flyweights[named] = new El.Flyweight();
10859         }
10860         El._flyweights[named].dom = el;
10861         return El._flyweights[named];
10862     };
10863
10864     /**
10865      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10866      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10867      * Shorthand of {@link Roo.Element#get}
10868      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10869      * @return {Element} The Element object
10870      * @member Roo
10871      * @method get
10872      */
10873     Roo.get = El.get;
10874     /**
10875      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10876      * the dom node can be overwritten by other code.
10877      * Shorthand of {@link Roo.Element#fly}
10878      * @param {String/HTMLElement} el The dom node or id
10879      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10880      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10881      * @static
10882      * @return {Element} The shared Element object
10883      * @member Roo
10884      * @method fly
10885      */
10886     Roo.fly = El.fly;
10887
10888     // speedy lookup for elements never to box adjust
10889     var noBoxAdjust = Roo.isStrict ? {
10890         select:1
10891     } : {
10892         input:1, select:1, textarea:1
10893     };
10894     if(Roo.isIE || Roo.isGecko){
10895         noBoxAdjust['button'] = 1;
10896     }
10897
10898
10899     Roo.EventManager.on(window, 'unload', function(){
10900         delete El.cache;
10901         delete El._flyweights;
10902     });
10903 })();
10904
10905
10906
10907
10908 if(Roo.DomQuery){
10909     Roo.Element.selectorFunction = Roo.DomQuery.select;
10910 }
10911
10912 Roo.Element.select = function(selector, unique, root){
10913     var els;
10914     if(typeof selector == "string"){
10915         els = Roo.Element.selectorFunction(selector, root);
10916     }else if(selector.length !== undefined){
10917         els = selector;
10918     }else{
10919         throw "Invalid selector";
10920     }
10921     if(unique === true){
10922         return new Roo.CompositeElement(els);
10923     }else{
10924         return new Roo.CompositeElementLite(els);
10925     }
10926 };
10927 /**
10928  * Selects elements based on the passed CSS selector to enable working on them as 1.
10929  * @param {String/Array} selector The CSS selector or an array of elements
10930  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10931  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10932  * @return {CompositeElementLite/CompositeElement}
10933  * @member Roo
10934  * @method select
10935  */
10936 Roo.select = Roo.Element.select;
10937
10938
10939
10940
10941
10942
10943
10944
10945
10946
10947
10948
10949
10950
10951 /*
10952  * Based on:
10953  * Ext JS Library 1.1.1
10954  * Copyright(c) 2006-2007, Ext JS, LLC.
10955  *
10956  * Originally Released Under LGPL - original licence link has changed is not relivant.
10957  *
10958  * Fork - LGPL
10959  * <script type="text/javascript">
10960  */
10961
10962
10963
10964 //Notifies Element that fx methods are available
10965 Roo.enableFx = true;
10966
10967 /**
10968  * @class Roo.Fx
10969  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10970  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10971  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10972  * Element effects to work.</p><br/>
10973  *
10974  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10975  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10976  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10977  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10978  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10979  * expected results and should be done with care.</p><br/>
10980  *
10981  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10982  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10983 <pre>
10984 Value  Description
10985 -----  -----------------------------
10986 tl     The top left corner
10987 t      The center of the top edge
10988 tr     The top right corner
10989 l      The center of the left edge
10990 r      The center of the right edge
10991 bl     The bottom left corner
10992 b      The center of the bottom edge
10993 br     The bottom right corner
10994 </pre>
10995  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10996  * below are common options that can be passed to any Fx method.</b>
10997  * @cfg {Function} callback A function called when the effect is finished
10998  * @cfg {Object} scope The scope of the effect function
10999  * @cfg {String} easing A valid Easing value for the effect
11000  * @cfg {String} afterCls A css class to apply after the effect
11001  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11002  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11003  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11004  * effects that end with the element being visually hidden, ignored otherwise)
11005  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11006  * a function which returns such a specification that will be applied to the Element after the effect finishes
11007  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11008  * @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
11009  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11010  */
11011 Roo.Fx = {
11012         /**
11013          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11014          * origin for the slide effect.  This function automatically handles wrapping the element with
11015          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11016          * Usage:
11017          *<pre><code>
11018 // default: slide the element in from the top
11019 el.slideIn();
11020
11021 // custom: slide the element in from the right with a 2-second duration
11022 el.slideIn('r', { duration: 2 });
11023
11024 // common config options shown with default values
11025 el.slideIn('t', {
11026     easing: 'easeOut',
11027     duration: .5
11028 });
11029 </code></pre>
11030          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11031          * @param {Object} options (optional) Object literal with any of the Fx config options
11032          * @return {Roo.Element} The Element
11033          */
11034     slideIn : function(anchor, o){
11035         var el = this.getFxEl();
11036         o = o || {};
11037
11038         el.queueFx(o, function(){
11039
11040             anchor = anchor || "t";
11041
11042             // fix display to visibility
11043             this.fixDisplay();
11044
11045             // restore values after effect
11046             var r = this.getFxRestore();
11047             var b = this.getBox();
11048             // fixed size for slide
11049             this.setSize(b);
11050
11051             // wrap if needed
11052             var wrap = this.fxWrap(r.pos, o, "hidden");
11053
11054             var st = this.dom.style;
11055             st.visibility = "visible";
11056             st.position = "absolute";
11057
11058             // clear out temp styles after slide and unwrap
11059             var after = function(){
11060                 el.fxUnwrap(wrap, r.pos, o);
11061                 st.width = r.width;
11062                 st.height = r.height;
11063                 el.afterFx(o);
11064             };
11065             // time to calc the positions
11066             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11067
11068             switch(anchor.toLowerCase()){
11069                 case "t":
11070                     wrap.setSize(b.width, 0);
11071                     st.left = st.bottom = "0";
11072                     a = {height: bh};
11073                 break;
11074                 case "l":
11075                     wrap.setSize(0, b.height);
11076                     st.right = st.top = "0";
11077                     a = {width: bw};
11078                 break;
11079                 case "r":
11080                     wrap.setSize(0, b.height);
11081                     wrap.setX(b.right);
11082                     st.left = st.top = "0";
11083                     a = {width: bw, points: pt};
11084                 break;
11085                 case "b":
11086                     wrap.setSize(b.width, 0);
11087                     wrap.setY(b.bottom);
11088                     st.left = st.top = "0";
11089                     a = {height: bh, points: pt};
11090                 break;
11091                 case "tl":
11092                     wrap.setSize(0, 0);
11093                     st.right = st.bottom = "0";
11094                     a = {width: bw, height: bh};
11095                 break;
11096                 case "bl":
11097                     wrap.setSize(0, 0);
11098                     wrap.setY(b.y+b.height);
11099                     st.right = st.top = "0";
11100                     a = {width: bw, height: bh, points: pt};
11101                 break;
11102                 case "br":
11103                     wrap.setSize(0, 0);
11104                     wrap.setXY([b.right, b.bottom]);
11105                     st.left = st.top = "0";
11106                     a = {width: bw, height: bh, points: pt};
11107                 break;
11108                 case "tr":
11109                     wrap.setSize(0, 0);
11110                     wrap.setX(b.x+b.width);
11111                     st.left = st.bottom = "0";
11112                     a = {width: bw, height: bh, points: pt};
11113                 break;
11114             }
11115             this.dom.style.visibility = "visible";
11116             wrap.show();
11117
11118             arguments.callee.anim = wrap.fxanim(a,
11119                 o,
11120                 'motion',
11121                 .5,
11122                 'easeOut', after);
11123         });
11124         return this;
11125     },
11126     
11127         /**
11128          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11129          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11130          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11131          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11132          * wrapping the element with 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 out to the top
11136 el.slideOut();
11137
11138 // custom: slide the element out to the right with a 2-second duration
11139 el.slideOut('r', { duration: 2 });
11140
11141 // common config options shown with default values
11142 el.slideOut('t', {
11143     easing: 'easeOut',
11144     duration: .5,
11145     remove: false,
11146     useDisplay: false
11147 });
11148 </code></pre>
11149          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11150          * @param {Object} options (optional) Object literal with any of the Fx config options
11151          * @return {Roo.Element} The Element
11152          */
11153     slideOut : function(anchor, o){
11154         var el = this.getFxEl();
11155         o = o || {};
11156
11157         el.queueFx(o, function(){
11158
11159             anchor = anchor || "t";
11160
11161             // restore values after effect
11162             var r = this.getFxRestore();
11163             
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, "visible");
11170
11171             var st = this.dom.style;
11172             st.visibility = "visible";
11173             st.position = "absolute";
11174
11175             wrap.setSize(b);
11176
11177             var after = function(){
11178                 if(o.useDisplay){
11179                     el.setDisplayed(false);
11180                 }else{
11181                     el.hide();
11182                 }
11183
11184                 el.fxUnwrap(wrap, r.pos, o);
11185
11186                 st.width = r.width;
11187                 st.height = r.height;
11188
11189                 el.afterFx(o);
11190             };
11191
11192             var a, zero = {to: 0};
11193             switch(anchor.toLowerCase()){
11194                 case "t":
11195                     st.left = st.bottom = "0";
11196                     a = {height: zero};
11197                 break;
11198                 case "l":
11199                     st.right = st.top = "0";
11200                     a = {width: zero};
11201                 break;
11202                 case "r":
11203                     st.left = st.top = "0";
11204                     a = {width: zero, points: {to:[b.right, b.y]}};
11205                 break;
11206                 case "b":
11207                     st.left = st.top = "0";
11208                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11209                 break;
11210                 case "tl":
11211                     st.right = st.bottom = "0";
11212                     a = {width: zero, height: zero};
11213                 break;
11214                 case "bl":
11215                     st.right = st.top = "0";
11216                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11217                 break;
11218                 case "br":
11219                     st.left = st.top = "0";
11220                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11221                 break;
11222                 case "tr":
11223                     st.left = st.bottom = "0";
11224                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11225                 break;
11226             }
11227
11228             arguments.callee.anim = wrap.fxanim(a,
11229                 o,
11230                 'motion',
11231                 .5,
11232                 "easeOut", after);
11233         });
11234         return this;
11235     },
11236
11237         /**
11238          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11239          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11240          * The element must be removed from the DOM using the 'remove' config option if desired.
11241          * Usage:
11242          *<pre><code>
11243 // default
11244 el.puff();
11245
11246 // common config options shown with default values
11247 el.puff({
11248     easing: 'easeOut',
11249     duration: .5,
11250     remove: false,
11251     useDisplay: false
11252 });
11253 </code></pre>
11254          * @param {Object} options (optional) Object literal with any of the Fx config options
11255          * @return {Roo.Element} The Element
11256          */
11257     puff : function(o){
11258         var el = this.getFxEl();
11259         o = o || {};
11260
11261         el.queueFx(o, function(){
11262             this.clearOpacity();
11263             this.show();
11264
11265             // restore values after effect
11266             var r = this.getFxRestore();
11267             var st = this.dom.style;
11268
11269             var after = function(){
11270                 if(o.useDisplay){
11271                     el.setDisplayed(false);
11272                 }else{
11273                     el.hide();
11274                 }
11275
11276                 el.clearOpacity();
11277
11278                 el.setPositioning(r.pos);
11279                 st.width = r.width;
11280                 st.height = r.height;
11281                 st.fontSize = '';
11282                 el.afterFx(o);
11283             };
11284
11285             var width = this.getWidth();
11286             var height = this.getHeight();
11287
11288             arguments.callee.anim = this.fxanim({
11289                     width : {to: this.adjustWidth(width * 2)},
11290                     height : {to: this.adjustHeight(height * 2)},
11291                     points : {by: [-(width * .5), -(height * .5)]},
11292                     opacity : {to: 0},
11293                     fontSize: {to:200, unit: "%"}
11294                 },
11295                 o,
11296                 'motion',
11297                 .5,
11298                 "easeOut", after);
11299         });
11300         return this;
11301     },
11302
11303         /**
11304          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11305          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11306          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11307          * Usage:
11308          *<pre><code>
11309 // default
11310 el.switchOff();
11311
11312 // all config options shown with default values
11313 el.switchOff({
11314     easing: 'easeIn',
11315     duration: .3,
11316     remove: false,
11317     useDisplay: false
11318 });
11319 </code></pre>
11320          * @param {Object} options (optional) Object literal with any of the Fx config options
11321          * @return {Roo.Element} The Element
11322          */
11323     switchOff : function(o){
11324         var el = this.getFxEl();
11325         o = o || {};
11326
11327         el.queueFx(o, function(){
11328             this.clearOpacity();
11329             this.clip();
11330
11331             // restore values after effect
11332             var r = this.getFxRestore();
11333             var st = this.dom.style;
11334
11335             var after = function(){
11336                 if(o.useDisplay){
11337                     el.setDisplayed(false);
11338                 }else{
11339                     el.hide();
11340                 }
11341
11342                 el.clearOpacity();
11343                 el.setPositioning(r.pos);
11344                 st.width = r.width;
11345                 st.height = r.height;
11346
11347                 el.afterFx(o);
11348             };
11349
11350             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11351                 this.clearOpacity();
11352                 (function(){
11353                     this.fxanim({
11354                         height:{to:1},
11355                         points:{by:[0, this.getHeight() * .5]}
11356                     }, o, 'motion', 0.3, 'easeIn', after);
11357                 }).defer(100, this);
11358             });
11359         });
11360         return this;
11361     },
11362
11363     /**
11364      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11365      * changed using the "attr" config option) and then fading back to the original color. If no original
11366      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11367      * Usage:
11368 <pre><code>
11369 // default: highlight background to yellow
11370 el.highlight();
11371
11372 // custom: highlight foreground text to blue for 2 seconds
11373 el.highlight("0000ff", { attr: 'color', duration: 2 });
11374
11375 // common config options shown with default values
11376 el.highlight("ffff9c", {
11377     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11378     endColor: (current color) or "ffffff",
11379     easing: 'easeIn',
11380     duration: 1
11381 });
11382 </code></pre>
11383      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11384      * @param {Object} options (optional) Object literal with any of the Fx config options
11385      * @return {Roo.Element} The Element
11386      */ 
11387     highlight : function(color, o){
11388         var el = this.getFxEl();
11389         o = o || {};
11390
11391         el.queueFx(o, function(){
11392             color = color || "ffff9c";
11393             attr = o.attr || "backgroundColor";
11394
11395             this.clearOpacity();
11396             this.show();
11397
11398             var origColor = this.getColor(attr);
11399             var restoreColor = this.dom.style[attr];
11400             endColor = (o.endColor || origColor) || "ffffff";
11401
11402             var after = function(){
11403                 el.dom.style[attr] = restoreColor;
11404                 el.afterFx(o);
11405             };
11406
11407             var a = {};
11408             a[attr] = {from: color, to: endColor};
11409             arguments.callee.anim = this.fxanim(a,
11410                 o,
11411                 'color',
11412                 1,
11413                 'easeIn', after);
11414         });
11415         return this;
11416     },
11417
11418    /**
11419     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11420     * Usage:
11421 <pre><code>
11422 // default: a single light blue ripple
11423 el.frame();
11424
11425 // custom: 3 red ripples lasting 3 seconds total
11426 el.frame("ff0000", 3, { duration: 3 });
11427
11428 // common config options shown with default values
11429 el.frame("C3DAF9", 1, {
11430     duration: 1 //duration of entire animation (not each individual ripple)
11431     // Note: Easing is not configurable and will be ignored if included
11432 });
11433 </code></pre>
11434     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11435     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11436     * @param {Object} options (optional) Object literal with any of the Fx config options
11437     * @return {Roo.Element} The Element
11438     */
11439     frame : function(color, count, o){
11440         var el = this.getFxEl();
11441         o = o || {};
11442
11443         el.queueFx(o, function(){
11444             color = color || "#C3DAF9";
11445             if(color.length == 6){
11446                 color = "#" + color;
11447             }
11448             count = count || 1;
11449             duration = o.duration || 1;
11450             this.show();
11451
11452             var b = this.getBox();
11453             var animFn = function(){
11454                 var proxy = this.createProxy({
11455
11456                      style:{
11457                         visbility:"hidden",
11458                         position:"absolute",
11459                         "z-index":"35000", // yee haw
11460                         border:"0px solid " + color
11461                      }
11462                   });
11463                 var scale = Roo.isBorderBox ? 2 : 1;
11464                 proxy.animate({
11465                     top:{from:b.y, to:b.y - 20},
11466                     left:{from:b.x, to:b.x - 20},
11467                     borderWidth:{from:0, to:10},
11468                     opacity:{from:1, to:0},
11469                     height:{from:b.height, to:(b.height + (20*scale))},
11470                     width:{from:b.width, to:(b.width + (20*scale))}
11471                 }, duration, function(){
11472                     proxy.remove();
11473                 });
11474                 if(--count > 0){
11475                      animFn.defer((duration/2)*1000, this);
11476                 }else{
11477                     el.afterFx(o);
11478                 }
11479             };
11480             animFn.call(this);
11481         });
11482         return this;
11483     },
11484
11485    /**
11486     * Creates a pause before any subsequent queued effects begin.  If there are
11487     * no effects queued after the pause it will have no effect.
11488     * Usage:
11489 <pre><code>
11490 el.pause(1);
11491 </code></pre>
11492     * @param {Number} seconds The length of time to pause (in seconds)
11493     * @return {Roo.Element} The Element
11494     */
11495     pause : function(seconds){
11496         var el = this.getFxEl();
11497         var o = {};
11498
11499         el.queueFx(o, function(){
11500             setTimeout(function(){
11501                 el.afterFx(o);
11502             }, seconds * 1000);
11503         });
11504         return this;
11505     },
11506
11507    /**
11508     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11509     * using the "endOpacity" config option.
11510     * Usage:
11511 <pre><code>
11512 // default: fade in from opacity 0 to 100%
11513 el.fadeIn();
11514
11515 // custom: fade in from opacity 0 to 75% over 2 seconds
11516 el.fadeIn({ endOpacity: .75, duration: 2});
11517
11518 // common config options shown with default values
11519 el.fadeIn({
11520     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11521     easing: 'easeOut',
11522     duration: .5
11523 });
11524 </code></pre>
11525     * @param {Object} options (optional) Object literal with any of the Fx config options
11526     * @return {Roo.Element} The Element
11527     */
11528     fadeIn : function(o){
11529         var el = this.getFxEl();
11530         o = o || {};
11531         el.queueFx(o, function(){
11532             this.setOpacity(0);
11533             this.fixDisplay();
11534             this.dom.style.visibility = 'visible';
11535             var to = o.endOpacity || 1;
11536             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11537                 o, null, .5, "easeOut", function(){
11538                 if(to == 1){
11539                     this.clearOpacity();
11540                 }
11541                 el.afterFx(o);
11542             });
11543         });
11544         return this;
11545     },
11546
11547    /**
11548     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11549     * using the "endOpacity" config option.
11550     * Usage:
11551 <pre><code>
11552 // default: fade out from the element's current opacity to 0
11553 el.fadeOut();
11554
11555 // custom: fade out from the element's current opacity to 25% over 2 seconds
11556 el.fadeOut({ endOpacity: .25, duration: 2});
11557
11558 // common config options shown with default values
11559 el.fadeOut({
11560     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11561     easing: 'easeOut',
11562     duration: .5
11563     remove: false,
11564     useDisplay: false
11565 });
11566 </code></pre>
11567     * @param {Object} options (optional) Object literal with any of the Fx config options
11568     * @return {Roo.Element} The Element
11569     */
11570     fadeOut : function(o){
11571         var el = this.getFxEl();
11572         o = o || {};
11573         el.queueFx(o, function(){
11574             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11575                 o, null, .5, "easeOut", function(){
11576                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11577                      this.dom.style.display = "none";
11578                 }else{
11579                      this.dom.style.visibility = "hidden";
11580                 }
11581                 this.clearOpacity();
11582                 el.afterFx(o);
11583             });
11584         });
11585         return this;
11586     },
11587
11588    /**
11589     * Animates the transition of an element's dimensions from a starting height/width
11590     * to an ending height/width.
11591     * Usage:
11592 <pre><code>
11593 // change height and width to 100x100 pixels
11594 el.scale(100, 100);
11595
11596 // common config options shown with default values.  The height and width will default to
11597 // the element's existing values if passed as null.
11598 el.scale(
11599     [element's width],
11600     [element's height], {
11601     easing: 'easeOut',
11602     duration: .35
11603 });
11604 </code></pre>
11605     * @param {Number} width  The new width (pass undefined to keep the original width)
11606     * @param {Number} height  The new height (pass undefined to keep the original height)
11607     * @param {Object} options (optional) Object literal with any of the Fx config options
11608     * @return {Roo.Element} The Element
11609     */
11610     scale : function(w, h, o){
11611         this.shift(Roo.apply({}, o, {
11612             width: w,
11613             height: h
11614         }));
11615         return this;
11616     },
11617
11618    /**
11619     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11620     * Any of these properties not specified in the config object will not be changed.  This effect 
11621     * requires that at least one new dimension, position or opacity setting must be passed in on
11622     * the config object in order for the function to have any effect.
11623     * Usage:
11624 <pre><code>
11625 // slide the element horizontally to x position 200 while changing the height and opacity
11626 el.shift({ x: 200, height: 50, opacity: .8 });
11627
11628 // common config options shown with default values.
11629 el.shift({
11630     width: [element's width],
11631     height: [element's height],
11632     x: [element's x position],
11633     y: [element's y position],
11634     opacity: [element's opacity],
11635     easing: 'easeOut',
11636     duration: .35
11637 });
11638 </code></pre>
11639     * @param {Object} options  Object literal with any of the Fx config options
11640     * @return {Roo.Element} The Element
11641     */
11642     shift : function(o){
11643         var el = this.getFxEl();
11644         o = o || {};
11645         el.queueFx(o, function(){
11646             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11647             if(w !== undefined){
11648                 a.width = {to: this.adjustWidth(w)};
11649             }
11650             if(h !== undefined){
11651                 a.height = {to: this.adjustHeight(h)};
11652             }
11653             if(x !== undefined || y !== undefined){
11654                 a.points = {to: [
11655                     x !== undefined ? x : this.getX(),
11656                     y !== undefined ? y : this.getY()
11657                 ]};
11658             }
11659             if(op !== undefined){
11660                 a.opacity = {to: op};
11661             }
11662             if(o.xy !== undefined){
11663                 a.points = {to: o.xy};
11664             }
11665             arguments.callee.anim = this.fxanim(a,
11666                 o, 'motion', .35, "easeOut", function(){
11667                 el.afterFx(o);
11668             });
11669         });
11670         return this;
11671     },
11672
11673         /**
11674          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11675          * ending point of the effect.
11676          * Usage:
11677          *<pre><code>
11678 // default: slide the element downward while fading out
11679 el.ghost();
11680
11681 // custom: slide the element out to the right with a 2-second duration
11682 el.ghost('r', { duration: 2 });
11683
11684 // common config options shown with default values
11685 el.ghost('b', {
11686     easing: 'easeOut',
11687     duration: .5
11688     remove: false,
11689     useDisplay: false
11690 });
11691 </code></pre>
11692          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11693          * @param {Object} options (optional) Object literal with any of the Fx config options
11694          * @return {Roo.Element} The Element
11695          */
11696     ghost : function(anchor, o){
11697         var el = this.getFxEl();
11698         o = o || {};
11699
11700         el.queueFx(o, function(){
11701             anchor = anchor || "b";
11702
11703             // restore values after effect
11704             var r = this.getFxRestore();
11705             var w = this.getWidth(),
11706                 h = this.getHeight();
11707
11708             var st = this.dom.style;
11709
11710             var after = function(){
11711                 if(o.useDisplay){
11712                     el.setDisplayed(false);
11713                 }else{
11714                     el.hide();
11715                 }
11716
11717                 el.clearOpacity();
11718                 el.setPositioning(r.pos);
11719                 st.width = r.width;
11720                 st.height = r.height;
11721
11722                 el.afterFx(o);
11723             };
11724
11725             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11726             switch(anchor.toLowerCase()){
11727                 case "t":
11728                     pt.by = [0, -h];
11729                 break;
11730                 case "l":
11731                     pt.by = [-w, 0];
11732                 break;
11733                 case "r":
11734                     pt.by = [w, 0];
11735                 break;
11736                 case "b":
11737                     pt.by = [0, h];
11738                 break;
11739                 case "tl":
11740                     pt.by = [-w, -h];
11741                 break;
11742                 case "bl":
11743                     pt.by = [-w, h];
11744                 break;
11745                 case "br":
11746                     pt.by = [w, h];
11747                 break;
11748                 case "tr":
11749                     pt.by = [w, -h];
11750                 break;
11751             }
11752
11753             arguments.callee.anim = this.fxanim(a,
11754                 o,
11755                 'motion',
11756                 .5,
11757                 "easeOut", after);
11758         });
11759         return this;
11760     },
11761
11762         /**
11763          * Ensures that all effects queued after syncFx is called on the element are
11764          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11765          * @return {Roo.Element} The Element
11766          */
11767     syncFx : function(){
11768         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11769             block : false,
11770             concurrent : true,
11771             stopFx : false
11772         });
11773         return this;
11774     },
11775
11776         /**
11777          * Ensures that all effects queued after sequenceFx is called on the element are
11778          * run in sequence.  This is the opposite of {@link #syncFx}.
11779          * @return {Roo.Element} The Element
11780          */
11781     sequenceFx : function(){
11782         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11783             block : false,
11784             concurrent : false,
11785             stopFx : false
11786         });
11787         return this;
11788     },
11789
11790         /* @private */
11791     nextFx : function(){
11792         var ef = this.fxQueue[0];
11793         if(ef){
11794             ef.call(this);
11795         }
11796     },
11797
11798         /**
11799          * Returns true if the element has any effects actively running or queued, else returns false.
11800          * @return {Boolean} True if element has active effects, else false
11801          */
11802     hasActiveFx : function(){
11803         return this.fxQueue && this.fxQueue[0];
11804     },
11805
11806         /**
11807          * Stops any running effects and clears the element's internal effects queue if it contains
11808          * any additional effects that haven't started yet.
11809          * @return {Roo.Element} The Element
11810          */
11811     stopFx : function(){
11812         if(this.hasActiveFx()){
11813             var cur = this.fxQueue[0];
11814             if(cur && cur.anim && cur.anim.isAnimated()){
11815                 this.fxQueue = [cur]; // clear out others
11816                 cur.anim.stop(true);
11817             }
11818         }
11819         return this;
11820     },
11821
11822         /* @private */
11823     beforeFx : function(o){
11824         if(this.hasActiveFx() && !o.concurrent){
11825            if(o.stopFx){
11826                this.stopFx();
11827                return true;
11828            }
11829            return false;
11830         }
11831         return true;
11832     },
11833
11834         /**
11835          * Returns true if the element is currently blocking so that no other effect can be queued
11836          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11837          * used to ensure that an effect initiated by a user action runs to completion prior to the
11838          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11839          * @return {Boolean} True if blocking, else false
11840          */
11841     hasFxBlock : function(){
11842         var q = this.fxQueue;
11843         return q && q[0] && q[0].block;
11844     },
11845
11846         /* @private */
11847     queueFx : function(o, fn){
11848         if(!this.fxQueue){
11849             this.fxQueue = [];
11850         }
11851         if(!this.hasFxBlock()){
11852             Roo.applyIf(o, this.fxDefaults);
11853             if(!o.concurrent){
11854                 var run = this.beforeFx(o);
11855                 fn.block = o.block;
11856                 this.fxQueue.push(fn);
11857                 if(run){
11858                     this.nextFx();
11859                 }
11860             }else{
11861                 fn.call(this);
11862             }
11863         }
11864         return this;
11865     },
11866
11867         /* @private */
11868     fxWrap : function(pos, o, vis){
11869         var wrap;
11870         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11871             var wrapXY;
11872             if(o.fixPosition){
11873                 wrapXY = this.getXY();
11874             }
11875             var div = document.createElement("div");
11876             div.style.visibility = vis;
11877             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11878             wrap.setPositioning(pos);
11879             if(wrap.getStyle("position") == "static"){
11880                 wrap.position("relative");
11881             }
11882             this.clearPositioning('auto');
11883             wrap.clip();
11884             wrap.dom.appendChild(this.dom);
11885             if(wrapXY){
11886                 wrap.setXY(wrapXY);
11887             }
11888         }
11889         return wrap;
11890     },
11891
11892         /* @private */
11893     fxUnwrap : function(wrap, pos, o){
11894         this.clearPositioning();
11895         this.setPositioning(pos);
11896         if(!o.wrap){
11897             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11898             wrap.remove();
11899         }
11900     },
11901
11902         /* @private */
11903     getFxRestore : function(){
11904         var st = this.dom.style;
11905         return {pos: this.getPositioning(), width: st.width, height : st.height};
11906     },
11907
11908         /* @private */
11909     afterFx : function(o){
11910         if(o.afterStyle){
11911             this.applyStyles(o.afterStyle);
11912         }
11913         if(o.afterCls){
11914             this.addClass(o.afterCls);
11915         }
11916         if(o.remove === true){
11917             this.remove();
11918         }
11919         Roo.callback(o.callback, o.scope, [this]);
11920         if(!o.concurrent){
11921             this.fxQueue.shift();
11922             this.nextFx();
11923         }
11924     },
11925
11926         /* @private */
11927     getFxEl : function(){ // support for composite element fx
11928         return Roo.get(this.dom);
11929     },
11930
11931         /* @private */
11932     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11933         animType = animType || 'run';
11934         opt = opt || {};
11935         var anim = Roo.lib.Anim[animType](
11936             this.dom, args,
11937             (opt.duration || defaultDur) || .35,
11938             (opt.easing || defaultEase) || 'easeOut',
11939             function(){
11940                 Roo.callback(cb, this);
11941             },
11942             this
11943         );
11944         opt.anim = anim;
11945         return anim;
11946     }
11947 };
11948
11949 // backwords compat
11950 Roo.Fx.resize = Roo.Fx.scale;
11951
11952 //When included, Roo.Fx is automatically applied to Element so that all basic
11953 //effects are available directly via the Element API
11954 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11955  * Based on:
11956  * Ext JS Library 1.1.1
11957  * Copyright(c) 2006-2007, Ext JS, LLC.
11958  *
11959  * Originally Released Under LGPL - original licence link has changed is not relivant.
11960  *
11961  * Fork - LGPL
11962  * <script type="text/javascript">
11963  */
11964
11965
11966 /**
11967  * @class Roo.CompositeElement
11968  * Standard composite class. Creates a Roo.Element for every element in the collection.
11969  * <br><br>
11970  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11971  * actions will be performed on all the elements in this collection.</b>
11972  * <br><br>
11973  * All methods return <i>this</i> and can be chained.
11974  <pre><code>
11975  var els = Roo.select("#some-el div.some-class", true);
11976  // or select directly from an existing element
11977  var el = Roo.get('some-el');
11978  el.select('div.some-class', true);
11979
11980  els.setWidth(100); // all elements become 100 width
11981  els.hide(true); // all elements fade out and hide
11982  // or
11983  els.setWidth(100).hide(true);
11984  </code></pre>
11985  */
11986 Roo.CompositeElement = function(els){
11987     this.elements = [];
11988     this.addElements(els);
11989 };
11990 Roo.CompositeElement.prototype = {
11991     isComposite: true,
11992     addElements : function(els){
11993         if(!els) {
11994             return this;
11995         }
11996         if(typeof els == "string"){
11997             els = Roo.Element.selectorFunction(els);
11998         }
11999         var yels = this.elements;
12000         var index = yels.length-1;
12001         for(var i = 0, len = els.length; i < len; i++) {
12002                 yels[++index] = Roo.get(els[i]);
12003         }
12004         return this;
12005     },
12006
12007     /**
12008     * Clears this composite and adds the elements returned by the passed selector.
12009     * @param {String/Array} els A string CSS selector, an array of elements or an element
12010     * @return {CompositeElement} this
12011     */
12012     fill : function(els){
12013         this.elements = [];
12014         this.add(els);
12015         return this;
12016     },
12017
12018     /**
12019     * Filters this composite to only elements that match the passed selector.
12020     * @param {String} selector A string CSS selector
12021     * @param {Boolean} inverse return inverse filter (not matches)
12022     * @return {CompositeElement} this
12023     */
12024     filter : function(selector, inverse){
12025         var els = [];
12026         inverse = inverse || false;
12027         this.each(function(el){
12028             var match = inverse ? !el.is(selector) : el.is(selector);
12029             if(match){
12030                 els[els.length] = el.dom;
12031             }
12032         });
12033         this.fill(els);
12034         return this;
12035     },
12036
12037     invoke : function(fn, args){
12038         var els = this.elements;
12039         for(var i = 0, len = els.length; i < len; i++) {
12040                 Roo.Element.prototype[fn].apply(els[i], args);
12041         }
12042         return this;
12043     },
12044     /**
12045     * Adds elements to this composite.
12046     * @param {String/Array} els A string CSS selector, an array of elements or an element
12047     * @return {CompositeElement} this
12048     */
12049     add : function(els){
12050         if(typeof els == "string"){
12051             this.addElements(Roo.Element.selectorFunction(els));
12052         }else if(els.length !== undefined){
12053             this.addElements(els);
12054         }else{
12055             this.addElements([els]);
12056         }
12057         return this;
12058     },
12059     /**
12060     * Calls the passed function passing (el, this, index) for each element in this composite.
12061     * @param {Function} fn The function to call
12062     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12063     * @return {CompositeElement} this
12064     */
12065     each : function(fn, scope){
12066         var els = this.elements;
12067         for(var i = 0, len = els.length; i < len; i++){
12068             if(fn.call(scope || els[i], els[i], this, i) === false) {
12069                 break;
12070             }
12071         }
12072         return this;
12073     },
12074
12075     /**
12076      * Returns the Element object at the specified index
12077      * @param {Number} index
12078      * @return {Roo.Element}
12079      */
12080     item : function(index){
12081         return this.elements[index] || null;
12082     },
12083
12084     /**
12085      * Returns the first Element
12086      * @return {Roo.Element}
12087      */
12088     first : function(){
12089         return this.item(0);
12090     },
12091
12092     /**
12093      * Returns the last Element
12094      * @return {Roo.Element}
12095      */
12096     last : function(){
12097         return this.item(this.elements.length-1);
12098     },
12099
12100     /**
12101      * Returns the number of elements in this composite
12102      * @return Number
12103      */
12104     getCount : function(){
12105         return this.elements.length;
12106     },
12107
12108     /**
12109      * Returns true if this composite contains the passed element
12110      * @return Boolean
12111      */
12112     contains : function(el){
12113         return this.indexOf(el) !== -1;
12114     },
12115
12116     /**
12117      * Returns true if this composite contains the passed element
12118      * @return Boolean
12119      */
12120     indexOf : function(el){
12121         return this.elements.indexOf(Roo.get(el));
12122     },
12123
12124
12125     /**
12126     * Removes the specified element(s).
12127     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12128     * or an array of any of those.
12129     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12130     * @return {CompositeElement} this
12131     */
12132     removeElement : function(el, removeDom){
12133         if(el instanceof Array){
12134             for(var i = 0, len = el.length; i < len; i++){
12135                 this.removeElement(el[i]);
12136             }
12137             return this;
12138         }
12139         var index = typeof el == 'number' ? el : this.indexOf(el);
12140         if(index !== -1){
12141             if(removeDom){
12142                 var d = this.elements[index];
12143                 if(d.dom){
12144                     d.remove();
12145                 }else{
12146                     d.parentNode.removeChild(d);
12147                 }
12148             }
12149             this.elements.splice(index, 1);
12150         }
12151         return this;
12152     },
12153
12154     /**
12155     * Replaces the specified element with the passed element.
12156     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12157     * to replace.
12158     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12159     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12160     * @return {CompositeElement} this
12161     */
12162     replaceElement : function(el, replacement, domReplace){
12163         var index = typeof el == 'number' ? el : this.indexOf(el);
12164         if(index !== -1){
12165             if(domReplace){
12166                 this.elements[index].replaceWith(replacement);
12167             }else{
12168                 this.elements.splice(index, 1, Roo.get(replacement))
12169             }
12170         }
12171         return this;
12172     },
12173
12174     /**
12175      * Removes all elements.
12176      */
12177     clear : function(){
12178         this.elements = [];
12179     }
12180 };
12181 (function(){
12182     Roo.CompositeElement.createCall = function(proto, fnName){
12183         if(!proto[fnName]){
12184             proto[fnName] = function(){
12185                 return this.invoke(fnName, arguments);
12186             };
12187         }
12188     };
12189     for(var fnName in Roo.Element.prototype){
12190         if(typeof Roo.Element.prototype[fnName] == "function"){
12191             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12192         }
12193     };
12194 })();
12195 /*
12196  * Based on:
12197  * Ext JS Library 1.1.1
12198  * Copyright(c) 2006-2007, Ext JS, LLC.
12199  *
12200  * Originally Released Under LGPL - original licence link has changed is not relivant.
12201  *
12202  * Fork - LGPL
12203  * <script type="text/javascript">
12204  */
12205
12206 /**
12207  * @class Roo.CompositeElementLite
12208  * @extends Roo.CompositeElement
12209  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12210  <pre><code>
12211  var els = Roo.select("#some-el div.some-class");
12212  // or select directly from an existing element
12213  var el = Roo.get('some-el');
12214  el.select('div.some-class');
12215
12216  els.setWidth(100); // all elements become 100 width
12217  els.hide(true); // all elements fade out and hide
12218  // or
12219  els.setWidth(100).hide(true);
12220  </code></pre><br><br>
12221  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12222  * actions will be performed on all the elements in this collection.</b>
12223  */
12224 Roo.CompositeElementLite = function(els){
12225     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12226     this.el = new Roo.Element.Flyweight();
12227 };
12228 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12229     addElements : function(els){
12230         if(els){
12231             if(els instanceof Array){
12232                 this.elements = this.elements.concat(els);
12233             }else{
12234                 var yels = this.elements;
12235                 var index = yels.length-1;
12236                 for(var i = 0, len = els.length; i < len; i++) {
12237                     yels[++index] = els[i];
12238                 }
12239             }
12240         }
12241         return this;
12242     },
12243     invoke : function(fn, args){
12244         var els = this.elements;
12245         var el = this.el;
12246         for(var i = 0, len = els.length; i < len; i++) {
12247             el.dom = els[i];
12248                 Roo.Element.prototype[fn].apply(el, args);
12249         }
12250         return this;
12251     },
12252     /**
12253      * Returns a flyweight Element of the dom element object at the specified index
12254      * @param {Number} index
12255      * @return {Roo.Element}
12256      */
12257     item : function(index){
12258         if(!this.elements[index]){
12259             return null;
12260         }
12261         this.el.dom = this.elements[index];
12262         return this.el;
12263     },
12264
12265     // fixes scope with flyweight
12266     addListener : function(eventName, handler, scope, opt){
12267         var els = this.elements;
12268         for(var i = 0, len = els.length; i < len; i++) {
12269             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12270         }
12271         return this;
12272     },
12273
12274     /**
12275     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12276     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12277     * a reference to the dom node, use el.dom.</b>
12278     * @param {Function} fn The function to call
12279     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12280     * @return {CompositeElement} this
12281     */
12282     each : function(fn, scope){
12283         var els = this.elements;
12284         var el = this.el;
12285         for(var i = 0, len = els.length; i < len; i++){
12286             el.dom = els[i];
12287                 if(fn.call(scope || el, el, this, i) === false){
12288                 break;
12289             }
12290         }
12291         return this;
12292     },
12293
12294     indexOf : function(el){
12295         return this.elements.indexOf(Roo.getDom(el));
12296     },
12297
12298     replaceElement : function(el, replacement, domReplace){
12299         var index = typeof el == 'number' ? el : this.indexOf(el);
12300         if(index !== -1){
12301             replacement = Roo.getDom(replacement);
12302             if(domReplace){
12303                 var d = this.elements[index];
12304                 d.parentNode.insertBefore(replacement, d);
12305                 d.parentNode.removeChild(d);
12306             }
12307             this.elements.splice(index, 1, replacement);
12308         }
12309         return this;
12310     }
12311 });
12312 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12313
12314 /*
12315  * Based on:
12316  * Ext JS Library 1.1.1
12317  * Copyright(c) 2006-2007, Ext JS, LLC.
12318  *
12319  * Originally Released Under LGPL - original licence link has changed is not relivant.
12320  *
12321  * Fork - LGPL
12322  * <script type="text/javascript">
12323  */
12324
12325  
12326
12327 /**
12328  * @class Roo.data.Connection
12329  * @extends Roo.util.Observable
12330  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12331  * either to a configured URL, or to a URL specified at request time. 
12332  * 
12333  * Requests made by this class are asynchronous, and will return immediately. No data from
12334  * the server will be available to the statement immediately following the {@link #request} call.
12335  * To process returned data, use a callback in the request options object, or an event listener.
12336  * 
12337  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12338  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12339  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12340  * property and, if present, the IFRAME's XML document as the responseXML property.
12341  * 
12342  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12343  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12344  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12345  * standard DOM methods.
12346  * @constructor
12347  * @param {Object} config a configuration object.
12348  */
12349 Roo.data.Connection = function(config){
12350     Roo.apply(this, config);
12351     this.addEvents({
12352         /**
12353          * @event beforerequest
12354          * Fires before a network request is made to retrieve a data object.
12355          * @param {Connection} conn This Connection object.
12356          * @param {Object} options The options config object passed to the {@link #request} method.
12357          */
12358         "beforerequest" : true,
12359         /**
12360          * @event requestcomplete
12361          * Fires if the request was successfully completed.
12362          * @param {Connection} conn This Connection object.
12363          * @param {Object} response The XHR object containing the response data.
12364          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12365          * @param {Object} options The options config object passed to the {@link #request} method.
12366          */
12367         "requestcomplete" : true,
12368         /**
12369          * @event requestexception
12370          * Fires if an error HTTP status was returned from the server.
12371          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12372          * @param {Connection} conn This Connection object.
12373          * @param {Object} response The XHR object containing the response data.
12374          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12375          * @param {Object} options The options config object passed to the {@link #request} method.
12376          */
12377         "requestexception" : true
12378     });
12379     Roo.data.Connection.superclass.constructor.call(this);
12380 };
12381
12382 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12383     /**
12384      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12385      */
12386     /**
12387      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12388      * extra parameters to each request made by this object. (defaults to undefined)
12389      */
12390     /**
12391      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12392      *  to each request made by this object. (defaults to undefined)
12393      */
12394     /**
12395      * @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)
12396      */
12397     /**
12398      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12399      */
12400     timeout : 30000,
12401     /**
12402      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12403      * @type Boolean
12404      */
12405     autoAbort:false,
12406
12407     /**
12408      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12409      * @type Boolean
12410      */
12411     disableCaching: true,
12412
12413     /**
12414      * Sends an HTTP request to a remote server.
12415      * @param {Object} options An object which may contain the following properties:<ul>
12416      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12417      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12418      * request, a url encoded string or a function to call to get either.</li>
12419      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12420      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12421      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12422      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12423      * <li>options {Object} The parameter to the request call.</li>
12424      * <li>success {Boolean} True if the request succeeded.</li>
12425      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12426      * </ul></li>
12427      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12428      * The callback is passed the following parameters:<ul>
12429      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12430      * <li>options {Object} The parameter to the request call.</li>
12431      * </ul></li>
12432      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12433      * The callback is passed the following parameters:<ul>
12434      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12435      * <li>options {Object} The parameter to the request call.</li>
12436      * </ul></li>
12437      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12438      * for the callback function. Defaults to the browser window.</li>
12439      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12440      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12441      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12442      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12443      * params for the post data. Any params will be appended to the URL.</li>
12444      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12445      * </ul>
12446      * @return {Number} transactionId
12447      */
12448     request : function(o){
12449         if(this.fireEvent("beforerequest", this, o) !== false){
12450             var p = o.params;
12451
12452             if(typeof p == "function"){
12453                 p = p.call(o.scope||window, o);
12454             }
12455             if(typeof p == "object"){
12456                 p = Roo.urlEncode(o.params);
12457             }
12458             if(this.extraParams){
12459                 var extras = Roo.urlEncode(this.extraParams);
12460                 p = p ? (p + '&' + extras) : extras;
12461             }
12462
12463             var url = o.url || this.url;
12464             if(typeof url == 'function'){
12465                 url = url.call(o.scope||window, o);
12466             }
12467
12468             if(o.form){
12469                 var form = Roo.getDom(o.form);
12470                 url = url || form.action;
12471
12472                 var enctype = form.getAttribute("enctype");
12473                 
12474                 if (o.formData) {
12475                     return this.doFormDataUpload(o, url);
12476                 }
12477                 
12478                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12479                     return this.doFormUpload(o, p, url);
12480                 }
12481                 var f = Roo.lib.Ajax.serializeForm(form);
12482                 p = p ? (p + '&' + f) : f;
12483             }
12484             
12485             if (!o.form && o.formData) {
12486                 o.formData = o.formData === true ? new FormData() : o.formData;
12487                 for (var k in o.params) {
12488                     o.formData.append(k,o.params[k]);
12489                 }
12490                     
12491                 return this.doFormDataUpload(o, url);
12492             }
12493             
12494
12495             var hs = o.headers;
12496             if(this.defaultHeaders){
12497                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12498                 if(!o.headers){
12499                     o.headers = hs;
12500                 }
12501             }
12502
12503             var cb = {
12504                 success: this.handleResponse,
12505                 failure: this.handleFailure,
12506                 scope: this,
12507                 argument: {options: o},
12508                 timeout : o.timeout || this.timeout
12509             };
12510
12511             var method = o.method||this.method||(p ? "POST" : "GET");
12512
12513             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12514                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12515             }
12516
12517             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12518                 if(o.autoAbort){
12519                     this.abort();
12520                 }
12521             }else if(this.autoAbort !== false){
12522                 this.abort();
12523             }
12524
12525             if((method == 'GET' && p) || o.xmlData){
12526                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12527                 p = '';
12528             }
12529             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12530             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12531             Roo.lib.Ajax.useDefaultHeader == true;
12532             return this.transId;
12533         }else{
12534             Roo.callback(o.callback, o.scope, [o, null, null]);
12535             return null;
12536         }
12537     },
12538
12539     /**
12540      * Determine whether this object has a request outstanding.
12541      * @param {Number} transactionId (Optional) defaults to the last transaction
12542      * @return {Boolean} True if there is an outstanding request.
12543      */
12544     isLoading : function(transId){
12545         if(transId){
12546             return Roo.lib.Ajax.isCallInProgress(transId);
12547         }else{
12548             return this.transId ? true : false;
12549         }
12550     },
12551
12552     /**
12553      * Aborts any outstanding request.
12554      * @param {Number} transactionId (Optional) defaults to the last transaction
12555      */
12556     abort : function(transId){
12557         if(transId || this.isLoading()){
12558             Roo.lib.Ajax.abort(transId || this.transId);
12559         }
12560     },
12561
12562     // private
12563     handleResponse : function(response){
12564         this.transId = false;
12565         var options = response.argument.options;
12566         response.argument = options ? options.argument : null;
12567         this.fireEvent("requestcomplete", this, response, options);
12568         Roo.callback(options.success, options.scope, [response, options]);
12569         Roo.callback(options.callback, options.scope, [options, true, response]);
12570     },
12571
12572     // private
12573     handleFailure : function(response, e){
12574         this.transId = false;
12575         var options = response.argument.options;
12576         response.argument = options ? options.argument : null;
12577         this.fireEvent("requestexception", this, response, options, e);
12578         Roo.callback(options.failure, options.scope, [response, options]);
12579         Roo.callback(options.callback, options.scope, [options, false, response]);
12580     },
12581
12582     // private
12583     doFormUpload : function(o, ps, url){
12584         var id = Roo.id();
12585         var frame = document.createElement('iframe');
12586         frame.id = id;
12587         frame.name = id;
12588         frame.className = 'x-hidden';
12589         if(Roo.isIE){
12590             frame.src = Roo.SSL_SECURE_URL;
12591         }
12592         document.body.appendChild(frame);
12593
12594         if(Roo.isIE){
12595            document.frames[id].name = id;
12596         }
12597
12598         var form = Roo.getDom(o.form);
12599         form.target = id;
12600         form.method = 'POST';
12601         form.enctype = form.encoding = 'multipart/form-data';
12602         if(url){
12603             form.action = url;
12604         }
12605
12606         var hiddens, hd;
12607         if(ps){ // add dynamic params
12608             hiddens = [];
12609             ps = Roo.urlDecode(ps, false);
12610             for(var k in ps){
12611                 if(ps.hasOwnProperty(k)){
12612                     hd = document.createElement('input');
12613                     hd.type = 'hidden';
12614                     hd.name = k;
12615                     hd.value = ps[k];
12616                     form.appendChild(hd);
12617                     hiddens.push(hd);
12618                 }
12619             }
12620         }
12621
12622         function cb(){
12623             var r = {  // bogus response object
12624                 responseText : '',
12625                 responseXML : null
12626             };
12627
12628             r.argument = o ? o.argument : null;
12629
12630             try { //
12631                 var doc;
12632                 if(Roo.isIE){
12633                     doc = frame.contentWindow.document;
12634                 }else {
12635                     doc = (frame.contentDocument || window.frames[id].document);
12636                 }
12637                 if(doc && doc.body){
12638                     r.responseText = doc.body.innerHTML;
12639                 }
12640                 if(doc && doc.XMLDocument){
12641                     r.responseXML = doc.XMLDocument;
12642                 }else {
12643                     r.responseXML = doc;
12644                 }
12645             }
12646             catch(e) {
12647                 // ignore
12648             }
12649
12650             Roo.EventManager.removeListener(frame, 'load', cb, this);
12651
12652             this.fireEvent("requestcomplete", this, r, o);
12653             Roo.callback(o.success, o.scope, [r, o]);
12654             Roo.callback(o.callback, o.scope, [o, true, r]);
12655
12656             setTimeout(function(){document.body.removeChild(frame);}, 100);
12657         }
12658
12659         Roo.EventManager.on(frame, 'load', cb, this);
12660         form.submit();
12661
12662         if(hiddens){ // remove dynamic params
12663             for(var i = 0, len = hiddens.length; i < len; i++){
12664                 form.removeChild(hiddens[i]);
12665             }
12666         }
12667     },
12668     // this is a 'formdata version???'
12669     
12670     
12671     doFormDataUpload : function(o,  url)
12672     {
12673         var formData;
12674         if (o.form) {
12675             var form =  Roo.getDom(o.form);
12676             form.enctype = form.encoding = 'multipart/form-data';
12677             formData = o.formData === true ? new FormData(form) : o.formData;
12678         } else {
12679             formData = o.formData === true ? new FormData() : o.formData;
12680         }
12681         
12682       
12683         var cb = {
12684             success: this.handleResponse,
12685             failure: this.handleFailure,
12686             scope: this,
12687             argument: {options: o},
12688             timeout : o.timeout || this.timeout
12689         };
12690  
12691         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12692             if(o.autoAbort){
12693                 this.abort();
12694             }
12695         }else if(this.autoAbort !== false){
12696             this.abort();
12697         }
12698
12699         //Roo.lib.Ajax.defaultPostHeader = null;
12700         Roo.lib.Ajax.useDefaultHeader = false;
12701         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12702         Roo.lib.Ajax.useDefaultHeader = true;
12703  
12704          
12705     }
12706     
12707 });
12708 /*
12709  * Based on:
12710  * Ext JS Library 1.1.1
12711  * Copyright(c) 2006-2007, Ext JS, LLC.
12712  *
12713  * Originally Released Under LGPL - original licence link has changed is not relivant.
12714  *
12715  * Fork - LGPL
12716  * <script type="text/javascript">
12717  */
12718  
12719 /**
12720  * Global Ajax request class.
12721  * 
12722  * @class Roo.Ajax
12723  * @extends Roo.data.Connection
12724  * @static
12725  * 
12726  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12727  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12728  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12729  * @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)
12730  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12731  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12732  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12733  */
12734 Roo.Ajax = new Roo.data.Connection({
12735     // fix up the docs
12736     /**
12737      * @scope Roo.Ajax
12738      * @type {Boolear} 
12739      */
12740     autoAbort : false,
12741
12742     /**
12743      * Serialize the passed form into a url encoded string
12744      * @scope Roo.Ajax
12745      * @param {String/HTMLElement} form
12746      * @return {String}
12747      */
12748     serializeForm : function(form){
12749         return Roo.lib.Ajax.serializeForm(form);
12750     }
12751 });/*
12752  * Based on:
12753  * Ext JS Library 1.1.1
12754  * Copyright(c) 2006-2007, Ext JS, LLC.
12755  *
12756  * Originally Released Under LGPL - original licence link has changed is not relivant.
12757  *
12758  * Fork - LGPL
12759  * <script type="text/javascript">
12760  */
12761
12762  
12763 /**
12764  * @class Roo.UpdateManager
12765  * @extends Roo.util.Observable
12766  * Provides AJAX-style update for Element object.<br><br>
12767  * Usage:<br>
12768  * <pre><code>
12769  * // Get it from a Roo.Element object
12770  * var el = Roo.get("foo");
12771  * var mgr = el.getUpdateManager();
12772  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12773  * ...
12774  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12775  * <br>
12776  * // or directly (returns the same UpdateManager instance)
12777  * var mgr = new Roo.UpdateManager("myElementId");
12778  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12779  * mgr.on("update", myFcnNeedsToKnow);
12780  * <br>
12781    // short handed call directly from the element object
12782    Roo.get("foo").load({
12783         url: "bar.php",
12784         scripts:true,
12785         params: "for=bar",
12786         text: "Loading Foo..."
12787    });
12788  * </code></pre>
12789  * @constructor
12790  * Create new UpdateManager directly.
12791  * @param {String/HTMLElement/Roo.Element} el The element to update
12792  * @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).
12793  */
12794 Roo.UpdateManager = function(el, forceNew){
12795     el = Roo.get(el);
12796     if(!forceNew && el.updateManager){
12797         return el.updateManager;
12798     }
12799     /**
12800      * The Element object
12801      * @type Roo.Element
12802      */
12803     this.el = el;
12804     /**
12805      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12806      * @type String
12807      */
12808     this.defaultUrl = null;
12809
12810     this.addEvents({
12811         /**
12812          * @event beforeupdate
12813          * Fired before an update is made, return false from your handler and the update is cancelled.
12814          * @param {Roo.Element} el
12815          * @param {String/Object/Function} url
12816          * @param {String/Object} params
12817          */
12818         "beforeupdate": true,
12819         /**
12820          * @event update
12821          * Fired after successful update is made.
12822          * @param {Roo.Element} el
12823          * @param {Object} oResponseObject The response Object
12824          */
12825         "update": true,
12826         /**
12827          * @event failure
12828          * Fired on update failure.
12829          * @param {Roo.Element} el
12830          * @param {Object} oResponseObject The response Object
12831          */
12832         "failure": true
12833     });
12834     var d = Roo.UpdateManager.defaults;
12835     /**
12836      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12837      * @type String
12838      */
12839     this.sslBlankUrl = d.sslBlankUrl;
12840     /**
12841      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12842      * @type Boolean
12843      */
12844     this.disableCaching = d.disableCaching;
12845     /**
12846      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12847      * @type String
12848      */
12849     this.indicatorText = d.indicatorText;
12850     /**
12851      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12852      * @type String
12853      */
12854     this.showLoadIndicator = d.showLoadIndicator;
12855     /**
12856      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12857      * @type Number
12858      */
12859     this.timeout = d.timeout;
12860
12861     /**
12862      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12863      * @type Boolean
12864      */
12865     this.loadScripts = d.loadScripts;
12866
12867     /**
12868      * Transaction object of current executing transaction
12869      */
12870     this.transaction = null;
12871
12872     /**
12873      * @private
12874      */
12875     this.autoRefreshProcId = null;
12876     /**
12877      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12878      * @type Function
12879      */
12880     this.refreshDelegate = this.refresh.createDelegate(this);
12881     /**
12882      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12883      * @type Function
12884      */
12885     this.updateDelegate = this.update.createDelegate(this);
12886     /**
12887      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12888      * @type Function
12889      */
12890     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12891     /**
12892      * @private
12893      */
12894     this.successDelegate = this.processSuccess.createDelegate(this);
12895     /**
12896      * @private
12897      */
12898     this.failureDelegate = this.processFailure.createDelegate(this);
12899
12900     if(!this.renderer){
12901      /**
12902       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12903       */
12904     this.renderer = new Roo.UpdateManager.BasicRenderer();
12905     }
12906     
12907     Roo.UpdateManager.superclass.constructor.call(this);
12908 };
12909
12910 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12911     /**
12912      * Get the Element this UpdateManager is bound to
12913      * @return {Roo.Element} The element
12914      */
12915     getEl : function(){
12916         return this.el;
12917     },
12918     /**
12919      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12920      * @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:
12921 <pre><code>
12922 um.update({<br/>
12923     url: "your-url.php",<br/>
12924     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12925     callback: yourFunction,<br/>
12926     scope: yourObject, //(optional scope)  <br/>
12927     discardUrl: false, <br/>
12928     nocache: false,<br/>
12929     text: "Loading...",<br/>
12930     timeout: 30,<br/>
12931     scripts: false<br/>
12932 });
12933 </code></pre>
12934      * The only required property is url. The optional properties nocache, text and scripts
12935      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12936      * @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}
12937      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12938      * @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.
12939      */
12940     update : function(url, params, callback, discardUrl){
12941         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12942             var method = this.method,
12943                 cfg;
12944             if(typeof url == "object"){ // must be config object
12945                 cfg = url;
12946                 url = cfg.url;
12947                 params = params || cfg.params;
12948                 callback = callback || cfg.callback;
12949                 discardUrl = discardUrl || cfg.discardUrl;
12950                 if(callback && cfg.scope){
12951                     callback = callback.createDelegate(cfg.scope);
12952                 }
12953                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12954                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12955                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12956                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12957                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12958             }
12959             this.showLoading();
12960             if(!discardUrl){
12961                 this.defaultUrl = url;
12962             }
12963             if(typeof url == "function"){
12964                 url = url.call(this);
12965             }
12966
12967             method = method || (params ? "POST" : "GET");
12968             if(method == "GET"){
12969                 url = this.prepareUrl(url);
12970             }
12971
12972             var o = Roo.apply(cfg ||{}, {
12973                 url : url,
12974                 params: params,
12975                 success: this.successDelegate,
12976                 failure: this.failureDelegate,
12977                 callback: undefined,
12978                 timeout: (this.timeout*1000),
12979                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12980             });
12981             Roo.log("updated manager called with timeout of " + o.timeout);
12982             this.transaction = Roo.Ajax.request(o);
12983         }
12984     },
12985
12986     /**
12987      * 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.
12988      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12989      * @param {String/HTMLElement} form The form Id or form element
12990      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12991      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12992      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12993      */
12994     formUpdate : function(form, url, reset, callback){
12995         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12996             if(typeof url == "function"){
12997                 url = url.call(this);
12998             }
12999             form = Roo.getDom(form);
13000             this.transaction = Roo.Ajax.request({
13001                 form: form,
13002                 url:url,
13003                 success: this.successDelegate,
13004                 failure: this.failureDelegate,
13005                 timeout: (this.timeout*1000),
13006                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13007             });
13008             this.showLoading.defer(1, this);
13009         }
13010     },
13011
13012     /**
13013      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13014      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13015      */
13016     refresh : function(callback){
13017         if(this.defaultUrl == null){
13018             return;
13019         }
13020         this.update(this.defaultUrl, null, callback, true);
13021     },
13022
13023     /**
13024      * Set this element to auto refresh.
13025      * @param {Number} interval How often to update (in seconds).
13026      * @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)
13027      * @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}
13028      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13029      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13030      */
13031     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13032         if(refreshNow){
13033             this.update(url || this.defaultUrl, params, callback, true);
13034         }
13035         if(this.autoRefreshProcId){
13036             clearInterval(this.autoRefreshProcId);
13037         }
13038         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13039     },
13040
13041     /**
13042      * Stop auto refresh on this element.
13043      */
13044      stopAutoRefresh : function(){
13045         if(this.autoRefreshProcId){
13046             clearInterval(this.autoRefreshProcId);
13047             delete this.autoRefreshProcId;
13048         }
13049     },
13050
13051     isAutoRefreshing : function(){
13052        return this.autoRefreshProcId ? true : false;
13053     },
13054     /**
13055      * Called to update the element to "Loading" state. Override to perform custom action.
13056      */
13057     showLoading : function(){
13058         if(this.showLoadIndicator){
13059             this.el.update(this.indicatorText);
13060         }
13061     },
13062
13063     /**
13064      * Adds unique parameter to query string if disableCaching = true
13065      * @private
13066      */
13067     prepareUrl : function(url){
13068         if(this.disableCaching){
13069             var append = "_dc=" + (new Date().getTime());
13070             if(url.indexOf("?") !== -1){
13071                 url += "&" + append;
13072             }else{
13073                 url += "?" + append;
13074             }
13075         }
13076         return url;
13077     },
13078
13079     /**
13080      * @private
13081      */
13082     processSuccess : function(response){
13083         this.transaction = null;
13084         if(response.argument.form && response.argument.reset){
13085             try{ // put in try/catch since some older FF releases had problems with this
13086                 response.argument.form.reset();
13087             }catch(e){}
13088         }
13089         if(this.loadScripts){
13090             this.renderer.render(this.el, response, this,
13091                 this.updateComplete.createDelegate(this, [response]));
13092         }else{
13093             this.renderer.render(this.el, response, this);
13094             this.updateComplete(response);
13095         }
13096     },
13097
13098     updateComplete : function(response){
13099         this.fireEvent("update", this.el, response);
13100         if(typeof response.argument.callback == "function"){
13101             response.argument.callback(this.el, true, response);
13102         }
13103     },
13104
13105     /**
13106      * @private
13107      */
13108     processFailure : function(response){
13109         this.transaction = null;
13110         this.fireEvent("failure", this.el, response);
13111         if(typeof response.argument.callback == "function"){
13112             response.argument.callback(this.el, false, response);
13113         }
13114     },
13115
13116     /**
13117      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13118      * @param {Object} renderer The object implementing the render() method
13119      */
13120     setRenderer : function(renderer){
13121         this.renderer = renderer;
13122     },
13123
13124     getRenderer : function(){
13125        return this.renderer;
13126     },
13127
13128     /**
13129      * Set the defaultUrl used for updates
13130      * @param {String/Function} defaultUrl The url or a function to call to get the url
13131      */
13132     setDefaultUrl : function(defaultUrl){
13133         this.defaultUrl = defaultUrl;
13134     },
13135
13136     /**
13137      * Aborts the executing transaction
13138      */
13139     abort : function(){
13140         if(this.transaction){
13141             Roo.Ajax.abort(this.transaction);
13142         }
13143     },
13144
13145     /**
13146      * Returns true if an update is in progress
13147      * @return {Boolean}
13148      */
13149     isUpdating : function(){
13150         if(this.transaction){
13151             return Roo.Ajax.isLoading(this.transaction);
13152         }
13153         return false;
13154     }
13155 });
13156
13157 /**
13158  * @class Roo.UpdateManager.defaults
13159  * @static (not really - but it helps the doc tool)
13160  * The defaults collection enables customizing the default properties of UpdateManager
13161  */
13162    Roo.UpdateManager.defaults = {
13163        /**
13164          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13165          * @type Number
13166          */
13167          timeout : 30,
13168
13169          /**
13170          * True to process scripts by default (Defaults to false).
13171          * @type Boolean
13172          */
13173         loadScripts : false,
13174
13175         /**
13176         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13177         * @type String
13178         */
13179         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13180         /**
13181          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13182          * @type Boolean
13183          */
13184         disableCaching : false,
13185         /**
13186          * Whether to show indicatorText when loading (Defaults to true).
13187          * @type Boolean
13188          */
13189         showLoadIndicator : true,
13190         /**
13191          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13192          * @type String
13193          */
13194         indicatorText : '<div class="loading-indicator">Loading...</div>'
13195    };
13196
13197 /**
13198  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13199  *Usage:
13200  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13201  * @param {String/HTMLElement/Roo.Element} el The element to update
13202  * @param {String} url The url
13203  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13204  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13205  * @static
13206  * @deprecated
13207  * @member Roo.UpdateManager
13208  */
13209 Roo.UpdateManager.updateElement = function(el, url, params, options){
13210     var um = Roo.get(el, true).getUpdateManager();
13211     Roo.apply(um, options);
13212     um.update(url, params, options ? options.callback : null);
13213 };
13214 // alias for backwards compat
13215 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13216 /**
13217  * @class Roo.UpdateManager.BasicRenderer
13218  * Default Content renderer. Updates the elements innerHTML with the responseText.
13219  */
13220 Roo.UpdateManager.BasicRenderer = function(){};
13221
13222 Roo.UpdateManager.BasicRenderer.prototype = {
13223     /**
13224      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13225      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13226      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13227      * @param {Roo.Element} el The element being rendered
13228      * @param {Object} response The YUI Connect response object
13229      * @param {UpdateManager} updateManager The calling update manager
13230      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13231      */
13232      render : function(el, response, updateManager, callback){
13233         el.update(response.responseText, updateManager.loadScripts, callback);
13234     }
13235 };
13236 /*
13237  * Based on:
13238  * Roo JS
13239  * (c)) Alan Knowles
13240  * Licence : LGPL
13241  */
13242
13243
13244 /**
13245  * @class Roo.DomTemplate
13246  * @extends Roo.Template
13247  * An effort at a dom based template engine..
13248  *
13249  * Similar to XTemplate, except it uses dom parsing to create the template..
13250  *
13251  * Supported features:
13252  *
13253  *  Tags:
13254
13255 <pre><code>
13256       {a_variable} - output encoded.
13257       {a_variable.format:("Y-m-d")} - call a method on the variable
13258       {a_variable:raw} - unencoded output
13259       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13260       {a_variable:this.method_on_template(...)} - call a method on the template object.
13261  
13262 </code></pre>
13263  *  The tpl tag:
13264 <pre><code>
13265         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13266         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13267         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13268         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13269   
13270 </code></pre>
13271  *      
13272  */
13273 Roo.DomTemplate = function()
13274 {
13275      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13276      if (this.html) {
13277         this.compile();
13278      }
13279 };
13280
13281
13282 Roo.extend(Roo.DomTemplate, Roo.Template, {
13283     /**
13284      * id counter for sub templates.
13285      */
13286     id : 0,
13287     /**
13288      * flag to indicate if dom parser is inside a pre,
13289      * it will strip whitespace if not.
13290      */
13291     inPre : false,
13292     
13293     /**
13294      * The various sub templates
13295      */
13296     tpls : false,
13297     
13298     
13299     
13300     /**
13301      *
13302      * basic tag replacing syntax
13303      * WORD:WORD()
13304      *
13305      * // you can fake an object call by doing this
13306      *  x.t:(test,tesT) 
13307      * 
13308      */
13309     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13310     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13311     
13312     iterChild : function (node, method) {
13313         
13314         var oldPre = this.inPre;
13315         if (node.tagName == 'PRE') {
13316             this.inPre = true;
13317         }
13318         for( var i = 0; i < node.childNodes.length; i++) {
13319             method.call(this, node.childNodes[i]);
13320         }
13321         this.inPre = oldPre;
13322     },
13323     
13324     
13325     
13326     /**
13327      * compile the template
13328      *
13329      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13330      *
13331      */
13332     compile: function()
13333     {
13334         var s = this.html;
13335         
13336         // covert the html into DOM...
13337         var doc = false;
13338         var div =false;
13339         try {
13340             doc = document.implementation.createHTMLDocument("");
13341             doc.documentElement.innerHTML =   this.html  ;
13342             div = doc.documentElement;
13343         } catch (e) {
13344             // old IE... - nasty -- it causes all sorts of issues.. with
13345             // images getting pulled from server..
13346             div = document.createElement('div');
13347             div.innerHTML = this.html;
13348         }
13349         //doc.documentElement.innerHTML = htmlBody
13350          
13351         
13352         
13353         this.tpls = [];
13354         var _t = this;
13355         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13356         
13357         var tpls = this.tpls;
13358         
13359         // create a top level template from the snippet..
13360         
13361         //Roo.log(div.innerHTML);
13362         
13363         var tpl = {
13364             uid : 'master',
13365             id : this.id++,
13366             attr : false,
13367             value : false,
13368             body : div.innerHTML,
13369             
13370             forCall : false,
13371             execCall : false,
13372             dom : div,
13373             isTop : true
13374             
13375         };
13376         tpls.unshift(tpl);
13377         
13378         
13379         // compile them...
13380         this.tpls = [];
13381         Roo.each(tpls, function(tp){
13382             this.compileTpl(tp);
13383             this.tpls[tp.id] = tp;
13384         }, this);
13385         
13386         this.master = tpls[0];
13387         return this;
13388         
13389         
13390     },
13391     
13392     compileNode : function(node, istop) {
13393         // test for
13394         //Roo.log(node);
13395         
13396         
13397         // skip anything not a tag..
13398         if (node.nodeType != 1) {
13399             if (node.nodeType == 3 && !this.inPre) {
13400                 // reduce white space..
13401                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13402                 
13403             }
13404             return;
13405         }
13406         
13407         var tpl = {
13408             uid : false,
13409             id : false,
13410             attr : false,
13411             value : false,
13412             body : '',
13413             
13414             forCall : false,
13415             execCall : false,
13416             dom : false,
13417             isTop : istop
13418             
13419             
13420         };
13421         
13422         
13423         switch(true) {
13424             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13425             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13426             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13427             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13428             // no default..
13429         }
13430         
13431         
13432         if (!tpl.attr) {
13433             // just itterate children..
13434             this.iterChild(node,this.compileNode);
13435             return;
13436         }
13437         tpl.uid = this.id++;
13438         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13439         node.removeAttribute('roo-'+ tpl.attr);
13440         if (tpl.attr != 'name') {
13441             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13442             node.parentNode.replaceChild(placeholder,  node);
13443         } else {
13444             
13445             var placeholder =  document.createElement('span');
13446             placeholder.className = 'roo-tpl-' + tpl.value;
13447             node.parentNode.replaceChild(placeholder,  node);
13448         }
13449         
13450         // parent now sees '{domtplXXXX}
13451         this.iterChild(node,this.compileNode);
13452         
13453         // we should now have node body...
13454         var div = document.createElement('div');
13455         div.appendChild(node);
13456         tpl.dom = node;
13457         // this has the unfortunate side effect of converting tagged attributes
13458         // eg. href="{...}" into %7C...%7D
13459         // this has been fixed by searching for those combo's although it's a bit hacky..
13460         
13461         
13462         tpl.body = div.innerHTML;
13463         
13464         
13465          
13466         tpl.id = tpl.uid;
13467         switch(tpl.attr) {
13468             case 'for' :
13469                 switch (tpl.value) {
13470                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13471                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13472                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13473                 }
13474                 break;
13475             
13476             case 'exec':
13477                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13478                 break;
13479             
13480             case 'if':     
13481                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13482                 break;
13483             
13484             case 'name':
13485                 tpl.id  = tpl.value; // replace non characters???
13486                 break;
13487             
13488         }
13489         
13490         
13491         this.tpls.push(tpl);
13492         
13493         
13494         
13495     },
13496     
13497     
13498     
13499     
13500     /**
13501      * Compile a segment of the template into a 'sub-template'
13502      *
13503      * 
13504      * 
13505      *
13506      */
13507     compileTpl : function(tpl)
13508     {
13509         var fm = Roo.util.Format;
13510         var useF = this.disableFormats !== true;
13511         
13512         var sep = Roo.isGecko ? "+\n" : ",\n";
13513         
13514         var undef = function(str) {
13515             Roo.debug && Roo.log("Property not found :"  + str);
13516             return '';
13517         };
13518           
13519         //Roo.log(tpl.body);
13520         
13521         
13522         
13523         var fn = function(m, lbrace, name, format, args)
13524         {
13525             //Roo.log("ARGS");
13526             //Roo.log(arguments);
13527             args = args ? args.replace(/\\'/g,"'") : args;
13528             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13529             if (typeof(format) == 'undefined') {
13530                 format =  'htmlEncode'; 
13531             }
13532             if (format == 'raw' ) {
13533                 format = false;
13534             }
13535             
13536             if(name.substr(0, 6) == 'domtpl'){
13537                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13538             }
13539             
13540             // build an array of options to determine if value is undefined..
13541             
13542             // basically get 'xxxx.yyyy' then do
13543             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13544             //    (function () { Roo.log("Property not found"); return ''; })() :
13545             //    ......
13546             
13547             var udef_ar = [];
13548             var lookfor = '';
13549             Roo.each(name.split('.'), function(st) {
13550                 lookfor += (lookfor.length ? '.': '') + st;
13551                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13552             });
13553             
13554             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13555             
13556             
13557             if(format && useF){
13558                 
13559                 args = args ? ',' + args : "";
13560                  
13561                 if(format.substr(0, 5) != "this."){
13562                     format = "fm." + format + '(';
13563                 }else{
13564                     format = 'this.call("'+ format.substr(5) + '", ';
13565                     args = ", values";
13566                 }
13567                 
13568                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13569             }
13570              
13571             if (args && args.length) {
13572                 // called with xxyx.yuu:(test,test)
13573                 // change to ()
13574                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13575             }
13576             // raw.. - :raw modifier..
13577             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13578             
13579         };
13580         var body;
13581         // branched to use + in gecko and [].join() in others
13582         if(Roo.isGecko){
13583             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13584                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13585                     "';};};";
13586         }else{
13587             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13588             body.push(tpl.body.replace(/(\r\n|\n)/g,
13589                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13590             body.push("'].join('');};};");
13591             body = body.join('');
13592         }
13593         
13594         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13595        
13596         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13597         eval(body);
13598         
13599         return this;
13600     },
13601      
13602     /**
13603      * same as applyTemplate, except it's done to one of the subTemplates
13604      * when using named templates, you can do:
13605      *
13606      * var str = pl.applySubTemplate('your-name', values);
13607      *
13608      * 
13609      * @param {Number} id of the template
13610      * @param {Object} values to apply to template
13611      * @param {Object} parent (normaly the instance of this object)
13612      */
13613     applySubTemplate : function(id, values, parent)
13614     {
13615         
13616         
13617         var t = this.tpls[id];
13618         
13619         
13620         try { 
13621             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13622                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13623                 return '';
13624             }
13625         } catch(e) {
13626             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13627             Roo.log(values);
13628           
13629             return '';
13630         }
13631         try { 
13632             
13633             if(t.execCall && t.execCall.call(this, values, parent)){
13634                 return '';
13635             }
13636         } catch(e) {
13637             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13638             Roo.log(values);
13639             return '';
13640         }
13641         
13642         try {
13643             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13644             parent = t.target ? values : parent;
13645             if(t.forCall && vs instanceof Array){
13646                 var buf = [];
13647                 for(var i = 0, len = vs.length; i < len; i++){
13648                     try {
13649                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13650                     } catch (e) {
13651                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13652                         Roo.log(e.body);
13653                         //Roo.log(t.compiled);
13654                         Roo.log(vs[i]);
13655                     }   
13656                 }
13657                 return buf.join('');
13658             }
13659         } catch (e) {
13660             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13661             Roo.log(values);
13662             return '';
13663         }
13664         try {
13665             return t.compiled.call(this, vs, parent);
13666         } catch (e) {
13667             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13668             Roo.log(e.body);
13669             //Roo.log(t.compiled);
13670             Roo.log(values);
13671             return '';
13672         }
13673     },
13674
13675    
13676
13677     applyTemplate : function(values){
13678         return this.master.compiled.call(this, values, {});
13679         //var s = this.subs;
13680     },
13681
13682     apply : function(){
13683         return this.applyTemplate.apply(this, arguments);
13684     }
13685
13686  });
13687
13688 Roo.DomTemplate.from = function(el){
13689     el = Roo.getDom(el);
13690     return new Roo.Domtemplate(el.value || el.innerHTML);
13691 };/*
13692  * Based on:
13693  * Ext JS Library 1.1.1
13694  * Copyright(c) 2006-2007, Ext JS, LLC.
13695  *
13696  * Originally Released Under LGPL - original licence link has changed is not relivant.
13697  *
13698  * Fork - LGPL
13699  * <script type="text/javascript">
13700  */
13701
13702 /**
13703  * @class Roo.util.DelayedTask
13704  * Provides a convenient method of performing setTimeout where a new
13705  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13706  * You can use this class to buffer
13707  * the keypress events for a certain number of milliseconds, and perform only if they stop
13708  * for that amount of time.
13709  * @constructor The parameters to this constructor serve as defaults and are not required.
13710  * @param {Function} fn (optional) The default function to timeout
13711  * @param {Object} scope (optional) The default scope of that timeout
13712  * @param {Array} args (optional) The default Array of arguments
13713  */
13714 Roo.util.DelayedTask = function(fn, scope, args){
13715     var id = null, d, t;
13716
13717     var call = function(){
13718         var now = new Date().getTime();
13719         if(now - t >= d){
13720             clearInterval(id);
13721             id = null;
13722             fn.apply(scope, args || []);
13723         }
13724     };
13725     /**
13726      * Cancels any pending timeout and queues a new one
13727      * @param {Number} delay The milliseconds to delay
13728      * @param {Function} newFn (optional) Overrides function passed to constructor
13729      * @param {Object} newScope (optional) Overrides scope passed to constructor
13730      * @param {Array} newArgs (optional) Overrides args passed to constructor
13731      */
13732     this.delay = function(delay, newFn, newScope, newArgs){
13733         if(id && delay != d){
13734             this.cancel();
13735         }
13736         d = delay;
13737         t = new Date().getTime();
13738         fn = newFn || fn;
13739         scope = newScope || scope;
13740         args = newArgs || args;
13741         if(!id){
13742             id = setInterval(call, d);
13743         }
13744     };
13745
13746     /**
13747      * Cancel the last queued timeout
13748      */
13749     this.cancel = function(){
13750         if(id){
13751             clearInterval(id);
13752             id = null;
13753         }
13754     };
13755 };/*
13756  * Based on:
13757  * Ext JS Library 1.1.1
13758  * Copyright(c) 2006-2007, Ext JS, LLC.
13759  *
13760  * Originally Released Under LGPL - original licence link has changed is not relivant.
13761  *
13762  * Fork - LGPL
13763  * <script type="text/javascript">
13764  */
13765 /**
13766  * @class Roo.util.TaskRunner
13767  * Manage background tasks - not sure why this is better that setInterval?
13768  * @static
13769  *
13770  */
13771  
13772 Roo.util.TaskRunner = function(interval){
13773     interval = interval || 10;
13774     var tasks = [], removeQueue = [];
13775     var id = 0;
13776     var running = false;
13777
13778     var stopThread = function(){
13779         running = false;
13780         clearInterval(id);
13781         id = 0;
13782     };
13783
13784     var startThread = function(){
13785         if(!running){
13786             running = true;
13787             id = setInterval(runTasks, interval);
13788         }
13789     };
13790
13791     var removeTask = function(task){
13792         removeQueue.push(task);
13793         if(task.onStop){
13794             task.onStop();
13795         }
13796     };
13797
13798     var runTasks = function(){
13799         if(removeQueue.length > 0){
13800             for(var i = 0, len = removeQueue.length; i < len; i++){
13801                 tasks.remove(removeQueue[i]);
13802             }
13803             removeQueue = [];
13804             if(tasks.length < 1){
13805                 stopThread();
13806                 return;
13807             }
13808         }
13809         var now = new Date().getTime();
13810         for(var i = 0, len = tasks.length; i < len; ++i){
13811             var t = tasks[i];
13812             var itime = now - t.taskRunTime;
13813             if(t.interval <= itime){
13814                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13815                 t.taskRunTime = now;
13816                 if(rt === false || t.taskRunCount === t.repeat){
13817                     removeTask(t);
13818                     return;
13819                 }
13820             }
13821             if(t.duration && t.duration <= (now - t.taskStartTime)){
13822                 removeTask(t);
13823             }
13824         }
13825     };
13826
13827     /**
13828      * Queues a new task.
13829      * @param {Object} task
13830      *
13831      * Task property : interval = how frequent to run.
13832      * Task object should implement
13833      * function run()
13834      * Task object may implement
13835      * function onStop()
13836      */
13837     this.start = function(task){
13838         tasks.push(task);
13839         task.taskStartTime = new Date().getTime();
13840         task.taskRunTime = 0;
13841         task.taskRunCount = 0;
13842         startThread();
13843         return task;
13844     };
13845     /**
13846      * Stop  new task.
13847      * @param {Object} task
13848      */
13849     this.stop = function(task){
13850         removeTask(task);
13851         return task;
13852     };
13853     /**
13854      * Stop all Tasks
13855      */
13856     this.stopAll = function(){
13857         stopThread();
13858         for(var i = 0, len = tasks.length; i < len; i++){
13859             if(tasks[i].onStop){
13860                 tasks[i].onStop();
13861             }
13862         }
13863         tasks = [];
13864         removeQueue = [];
13865     };
13866 };
13867
13868 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13869  * Based on:
13870  * Ext JS Library 1.1.1
13871  * Copyright(c) 2006-2007, Ext JS, LLC.
13872  *
13873  * Originally Released Under LGPL - original licence link has changed is not relivant.
13874  *
13875  * Fork - LGPL
13876  * <script type="text/javascript">
13877  */
13878
13879  
13880 /**
13881  * @class Roo.util.MixedCollection
13882  * @extends Roo.util.Observable
13883  * A Collection class that maintains both numeric indexes and keys and exposes events.
13884  * @constructor
13885  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13886  * collection (defaults to false)
13887  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13888  * and return the key value for that item.  This is used when available to look up the key on items that
13889  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13890  * equivalent to providing an implementation for the {@link #getKey} method.
13891  */
13892 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13893     this.items = [];
13894     this.map = {};
13895     this.keys = [];
13896     this.length = 0;
13897     this.addEvents({
13898         /**
13899          * @event clear
13900          * Fires when the collection is cleared.
13901          */
13902         "clear" : true,
13903         /**
13904          * @event add
13905          * Fires when an item is added to the collection.
13906          * @param {Number} index The index at which the item was added.
13907          * @param {Object} o The item added.
13908          * @param {String} key The key associated with the added item.
13909          */
13910         "add" : true,
13911         /**
13912          * @event replace
13913          * Fires when an item is replaced in the collection.
13914          * @param {String} key he key associated with the new added.
13915          * @param {Object} old The item being replaced.
13916          * @param {Object} new The new item.
13917          */
13918         "replace" : true,
13919         /**
13920          * @event remove
13921          * Fires when an item is removed from the collection.
13922          * @param {Object} o The item being removed.
13923          * @param {String} key (optional) The key associated with the removed item.
13924          */
13925         "remove" : true,
13926         "sort" : true
13927     });
13928     this.allowFunctions = allowFunctions === true;
13929     if(keyFn){
13930         this.getKey = keyFn;
13931     }
13932     Roo.util.MixedCollection.superclass.constructor.call(this);
13933 };
13934
13935 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13936     allowFunctions : false,
13937     
13938 /**
13939  * Adds an item to the collection.
13940  * @param {String} key The key to associate with the item
13941  * @param {Object} o The item to add.
13942  * @return {Object} The item added.
13943  */
13944     add : function(key, o){
13945         if(arguments.length == 1){
13946             o = arguments[0];
13947             key = this.getKey(o);
13948         }
13949         if(typeof key == "undefined" || key === null){
13950             this.length++;
13951             this.items.push(o);
13952             this.keys.push(null);
13953         }else{
13954             var old = this.map[key];
13955             if(old){
13956                 return this.replace(key, o);
13957             }
13958             this.length++;
13959             this.items.push(o);
13960             this.map[key] = o;
13961             this.keys.push(key);
13962         }
13963         this.fireEvent("add", this.length-1, o, key);
13964         return o;
13965     },
13966        
13967 /**
13968   * MixedCollection has a generic way to fetch keys if you implement getKey.
13969 <pre><code>
13970 // normal way
13971 var mc = new Roo.util.MixedCollection();
13972 mc.add(someEl.dom.id, someEl);
13973 mc.add(otherEl.dom.id, otherEl);
13974 //and so on
13975
13976 // using getKey
13977 var mc = new Roo.util.MixedCollection();
13978 mc.getKey = function(el){
13979    return el.dom.id;
13980 };
13981 mc.add(someEl);
13982 mc.add(otherEl);
13983
13984 // or via the constructor
13985 var mc = new Roo.util.MixedCollection(false, function(el){
13986    return el.dom.id;
13987 });
13988 mc.add(someEl);
13989 mc.add(otherEl);
13990 </code></pre>
13991  * @param o {Object} The item for which to find the key.
13992  * @return {Object} The key for the passed item.
13993  */
13994     getKey : function(o){
13995          return o.id; 
13996     },
13997    
13998 /**
13999  * Replaces an item in the collection.
14000  * @param {String} key The key associated with the item to replace, or the item to replace.
14001  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14002  * @return {Object}  The new item.
14003  */
14004     replace : function(key, o){
14005         if(arguments.length == 1){
14006             o = arguments[0];
14007             key = this.getKey(o);
14008         }
14009         var old = this.item(key);
14010         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14011              return this.add(key, o);
14012         }
14013         var index = this.indexOfKey(key);
14014         this.items[index] = o;
14015         this.map[key] = o;
14016         this.fireEvent("replace", key, old, o);
14017         return o;
14018     },
14019    
14020 /**
14021  * Adds all elements of an Array or an Object to the collection.
14022  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14023  * an Array of values, each of which are added to the collection.
14024  */
14025     addAll : function(objs){
14026         if(arguments.length > 1 || objs instanceof Array){
14027             var args = arguments.length > 1 ? arguments : objs;
14028             for(var i = 0, len = args.length; i < len; i++){
14029                 this.add(args[i]);
14030             }
14031         }else{
14032             for(var key in objs){
14033                 if(this.allowFunctions || typeof objs[key] != "function"){
14034                     this.add(key, objs[key]);
14035                 }
14036             }
14037         }
14038     },
14039    
14040 /**
14041  * Executes the specified function once for every item in the collection, passing each
14042  * item as the first and only parameter. returning false from the function will stop the iteration.
14043  * @param {Function} fn The function to execute for each item.
14044  * @param {Object} scope (optional) The scope in which to execute the function.
14045  */
14046     each : function(fn, scope){
14047         var items = [].concat(this.items); // each safe for removal
14048         for(var i = 0, len = items.length; i < len; i++){
14049             if(fn.call(scope || items[i], items[i], i, len) === false){
14050                 break;
14051             }
14052         }
14053     },
14054    
14055 /**
14056  * Executes the specified function once for every key in the collection, passing each
14057  * key, and its associated item as the first two parameters.
14058  * @param {Function} fn The function to execute for each item.
14059  * @param {Object} scope (optional) The scope in which to execute the function.
14060  */
14061     eachKey : function(fn, scope){
14062         for(var i = 0, len = this.keys.length; i < len; i++){
14063             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14064         }
14065     },
14066    
14067 /**
14068  * Returns the first item in the collection which elicits a true return value from the
14069  * passed selection function.
14070  * @param {Function} fn The selection function to execute for each item.
14071  * @param {Object} scope (optional) The scope in which to execute the function.
14072  * @return {Object} The first item in the collection which returned true from the selection function.
14073  */
14074     find : function(fn, scope){
14075         for(var i = 0, len = this.items.length; i < len; i++){
14076             if(fn.call(scope || window, this.items[i], this.keys[i])){
14077                 return this.items[i];
14078             }
14079         }
14080         return null;
14081     },
14082    
14083 /**
14084  * Inserts an item at the specified index in the collection.
14085  * @param {Number} index The index to insert the item at.
14086  * @param {String} key The key to associate with the new item, or the item itself.
14087  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14088  * @return {Object} The item inserted.
14089  */
14090     insert : function(index, key, o){
14091         if(arguments.length == 2){
14092             o = arguments[1];
14093             key = this.getKey(o);
14094         }
14095         if(index >= this.length){
14096             return this.add(key, o);
14097         }
14098         this.length++;
14099         this.items.splice(index, 0, o);
14100         if(typeof key != "undefined" && key != null){
14101             this.map[key] = o;
14102         }
14103         this.keys.splice(index, 0, key);
14104         this.fireEvent("add", index, o, key);
14105         return o;
14106     },
14107    
14108 /**
14109  * Removed an item from the collection.
14110  * @param {Object} o The item to remove.
14111  * @return {Object} The item removed.
14112  */
14113     remove : function(o){
14114         return this.removeAt(this.indexOf(o));
14115     },
14116    
14117 /**
14118  * Remove an item from a specified index in the collection.
14119  * @param {Number} index The index within the collection of the item to remove.
14120  */
14121     removeAt : function(index){
14122         if(index < this.length && index >= 0){
14123             this.length--;
14124             var o = this.items[index];
14125             this.items.splice(index, 1);
14126             var key = this.keys[index];
14127             if(typeof key != "undefined"){
14128                 delete this.map[key];
14129             }
14130             this.keys.splice(index, 1);
14131             this.fireEvent("remove", o, key);
14132         }
14133     },
14134    
14135 /**
14136  * Removed an item associated with the passed key fom the collection.
14137  * @param {String} key The key of the item to remove.
14138  */
14139     removeKey : function(key){
14140         return this.removeAt(this.indexOfKey(key));
14141     },
14142    
14143 /**
14144  * Returns the number of items in the collection.
14145  * @return {Number} the number of items in the collection.
14146  */
14147     getCount : function(){
14148         return this.length; 
14149     },
14150    
14151 /**
14152  * Returns index within the collection of the passed Object.
14153  * @param {Object} o The item to find the index of.
14154  * @return {Number} index of the item.
14155  */
14156     indexOf : function(o){
14157         if(!this.items.indexOf){
14158             for(var i = 0, len = this.items.length; i < len; i++){
14159                 if(this.items[i] == o) {
14160                     return i;
14161                 }
14162             }
14163             return -1;
14164         }else{
14165             return this.items.indexOf(o);
14166         }
14167     },
14168    
14169 /**
14170  * Returns index within the collection of the passed key.
14171  * @param {String} key The key to find the index of.
14172  * @return {Number} index of the key.
14173  */
14174     indexOfKey : function(key){
14175         if(!this.keys.indexOf){
14176             for(var i = 0, len = this.keys.length; i < len; i++){
14177                 if(this.keys[i] == key) {
14178                     return i;
14179                 }
14180             }
14181             return -1;
14182         }else{
14183             return this.keys.indexOf(key);
14184         }
14185     },
14186    
14187 /**
14188  * Returns the item associated with the passed key OR index. Key has priority over index.
14189  * @param {String/Number} key The key or index of the item.
14190  * @return {Object} The item associated with the passed key.
14191  */
14192     item : function(key){
14193         if (key === 'length') {
14194             return null;
14195         }
14196         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14197         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14198     },
14199     
14200 /**
14201  * Returns the item at the specified index.
14202  * @param {Number} index The index of the item.
14203  * @return {Object}
14204  */
14205     itemAt : function(index){
14206         return this.items[index];
14207     },
14208     
14209 /**
14210  * Returns the item associated with the passed key.
14211  * @param {String/Number} key The key of the item.
14212  * @return {Object} The item associated with the passed key.
14213  */
14214     key : function(key){
14215         return this.map[key];
14216     },
14217    
14218 /**
14219  * Returns true if the collection contains the passed Object as an item.
14220  * @param {Object} o  The Object to look for in the collection.
14221  * @return {Boolean} True if the collection contains the Object as an item.
14222  */
14223     contains : function(o){
14224         return this.indexOf(o) != -1;
14225     },
14226    
14227 /**
14228  * Returns true if the collection contains the passed Object as a key.
14229  * @param {String} key The key to look for in the collection.
14230  * @return {Boolean} True if the collection contains the Object as a key.
14231  */
14232     containsKey : function(key){
14233         return typeof this.map[key] != "undefined";
14234     },
14235    
14236 /**
14237  * Removes all items from the collection.
14238  */
14239     clear : function(){
14240         this.length = 0;
14241         this.items = [];
14242         this.keys = [];
14243         this.map = {};
14244         this.fireEvent("clear");
14245     },
14246    
14247 /**
14248  * Returns the first item in the collection.
14249  * @return {Object} the first item in the collection..
14250  */
14251     first : function(){
14252         return this.items[0]; 
14253     },
14254    
14255 /**
14256  * Returns the last item in the collection.
14257  * @return {Object} the last item in the collection..
14258  */
14259     last : function(){
14260         return this.items[this.length-1];   
14261     },
14262     
14263     _sort : function(property, dir, fn){
14264         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14265         fn = fn || function(a, b){
14266             return a-b;
14267         };
14268         var c = [], k = this.keys, items = this.items;
14269         for(var i = 0, len = items.length; i < len; i++){
14270             c[c.length] = {key: k[i], value: items[i], index: i};
14271         }
14272         c.sort(function(a, b){
14273             var v = fn(a[property], b[property]) * dsc;
14274             if(v == 0){
14275                 v = (a.index < b.index ? -1 : 1);
14276             }
14277             return v;
14278         });
14279         for(var i = 0, len = c.length; i < len; i++){
14280             items[i] = c[i].value;
14281             k[i] = c[i].key;
14282         }
14283         this.fireEvent("sort", this);
14284     },
14285     
14286     /**
14287      * Sorts this collection with the passed comparison function
14288      * @param {String} direction (optional) "ASC" or "DESC"
14289      * @param {Function} fn (optional) comparison function
14290      */
14291     sort : function(dir, fn){
14292         this._sort("value", dir, fn);
14293     },
14294     
14295     /**
14296      * Sorts this collection by keys
14297      * @param {String} direction (optional) "ASC" or "DESC"
14298      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14299      */
14300     keySort : function(dir, fn){
14301         this._sort("key", dir, fn || function(a, b){
14302             return String(a).toUpperCase()-String(b).toUpperCase();
14303         });
14304     },
14305     
14306     /**
14307      * Returns a range of items in this collection
14308      * @param {Number} startIndex (optional) defaults to 0
14309      * @param {Number} endIndex (optional) default to the last item
14310      * @return {Array} An array of items
14311      */
14312     getRange : function(start, end){
14313         var items = this.items;
14314         if(items.length < 1){
14315             return [];
14316         }
14317         start = start || 0;
14318         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14319         var r = [];
14320         if(start <= end){
14321             for(var i = start; i <= end; i++) {
14322                     r[r.length] = items[i];
14323             }
14324         }else{
14325             for(var i = start; i >= end; i--) {
14326                     r[r.length] = items[i];
14327             }
14328         }
14329         return r;
14330     },
14331         
14332     /**
14333      * Filter the <i>objects</i> in this collection by a specific property. 
14334      * Returns a new collection that has been filtered.
14335      * @param {String} property A property on your objects
14336      * @param {String/RegExp} value Either string that the property values 
14337      * should start with or a RegExp to test against the property
14338      * @return {MixedCollection} The new filtered collection
14339      */
14340     filter : function(property, value){
14341         if(!value.exec){ // not a regex
14342             value = String(value);
14343             if(value.length == 0){
14344                 return this.clone();
14345             }
14346             value = new RegExp("^" + Roo.escapeRe(value), "i");
14347         }
14348         return this.filterBy(function(o){
14349             return o && value.test(o[property]);
14350         });
14351         },
14352     
14353     /**
14354      * Filter by a function. * Returns a new collection that has been filtered.
14355      * The passed function will be called with each 
14356      * object in the collection. If the function returns true, the value is included 
14357      * otherwise it is filtered.
14358      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14359      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14360      * @return {MixedCollection} The new filtered collection
14361      */
14362     filterBy : function(fn, scope){
14363         var r = new Roo.util.MixedCollection();
14364         r.getKey = this.getKey;
14365         var k = this.keys, it = this.items;
14366         for(var i = 0, len = it.length; i < len; i++){
14367             if(fn.call(scope||this, it[i], k[i])){
14368                                 r.add(k[i], it[i]);
14369                         }
14370         }
14371         return r;
14372     },
14373     
14374     /**
14375      * Creates a duplicate of this collection
14376      * @return {MixedCollection}
14377      */
14378     clone : function(){
14379         var r = new Roo.util.MixedCollection();
14380         var k = this.keys, it = this.items;
14381         for(var i = 0, len = it.length; i < len; i++){
14382             r.add(k[i], it[i]);
14383         }
14384         r.getKey = this.getKey;
14385         return r;
14386     }
14387 });
14388 /**
14389  * Returns the item associated with the passed key or index.
14390  * @method
14391  * @param {String/Number} key The key or index of the item.
14392  * @return {Object} The item associated with the passed key.
14393  */
14394 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14395  * Based on:
14396  * Ext JS Library 1.1.1
14397  * Copyright(c) 2006-2007, Ext JS, LLC.
14398  *
14399  * Originally Released Under LGPL - original licence link has changed is not relivant.
14400  *
14401  * Fork - LGPL
14402  * <script type="text/javascript">
14403  */
14404 /**
14405  * @class Roo.util.JSON
14406  * Modified version of Douglas Crockford"s json.js that doesn"t
14407  * mess with the Object prototype 
14408  * http://www.json.org/js.html
14409  * @static
14410  */
14411 Roo.util.JSON = new (function(){
14412     var useHasOwn = {}.hasOwnProperty ? true : false;
14413     
14414     // crashes Safari in some instances
14415     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14416     
14417     var pad = function(n) {
14418         return n < 10 ? "0" + n : n;
14419     };
14420     
14421     var m = {
14422         "\b": '\\b',
14423         "\t": '\\t',
14424         "\n": '\\n',
14425         "\f": '\\f',
14426         "\r": '\\r',
14427         '"' : '\\"',
14428         "\\": '\\\\'
14429     };
14430
14431     var encodeString = function(s){
14432         if (/["\\\x00-\x1f]/.test(s)) {
14433             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14434                 var c = m[b];
14435                 if(c){
14436                     return c;
14437                 }
14438                 c = b.charCodeAt();
14439                 return "\\u00" +
14440                     Math.floor(c / 16).toString(16) +
14441                     (c % 16).toString(16);
14442             }) + '"';
14443         }
14444         return '"' + s + '"';
14445     };
14446     
14447     var encodeArray = function(o){
14448         var a = ["["], b, i, l = o.length, v;
14449             for (i = 0; i < l; i += 1) {
14450                 v = o[i];
14451                 switch (typeof v) {
14452                     case "undefined":
14453                     case "function":
14454                     case "unknown":
14455                         break;
14456                     default:
14457                         if (b) {
14458                             a.push(',');
14459                         }
14460                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14461                         b = true;
14462                 }
14463             }
14464             a.push("]");
14465             return a.join("");
14466     };
14467     
14468     var encodeDate = function(o){
14469         return '"' + o.getFullYear() + "-" +
14470                 pad(o.getMonth() + 1) + "-" +
14471                 pad(o.getDate()) + "T" +
14472                 pad(o.getHours()) + ":" +
14473                 pad(o.getMinutes()) + ":" +
14474                 pad(o.getSeconds()) + '"';
14475     };
14476     
14477     /**
14478      * Encodes an Object, Array or other value
14479      * @param {Mixed} o The variable to encode
14480      * @return {String} The JSON string
14481      */
14482     this.encode = function(o)
14483     {
14484         // should this be extended to fully wrap stringify..
14485         
14486         if(typeof o == "undefined" || o === null){
14487             return "null";
14488         }else if(o instanceof Array){
14489             return encodeArray(o);
14490         }else if(o instanceof Date){
14491             return encodeDate(o);
14492         }else if(typeof o == "string"){
14493             return encodeString(o);
14494         }else if(typeof o == "number"){
14495             return isFinite(o) ? String(o) : "null";
14496         }else if(typeof o == "boolean"){
14497             return String(o);
14498         }else {
14499             var a = ["{"], b, i, v;
14500             for (i in o) {
14501                 if(!useHasOwn || o.hasOwnProperty(i)) {
14502                     v = o[i];
14503                     switch (typeof v) {
14504                     case "undefined":
14505                     case "function":
14506                     case "unknown":
14507                         break;
14508                     default:
14509                         if(b){
14510                             a.push(',');
14511                         }
14512                         a.push(this.encode(i), ":",
14513                                 v === null ? "null" : this.encode(v));
14514                         b = true;
14515                     }
14516                 }
14517             }
14518             a.push("}");
14519             return a.join("");
14520         }
14521     };
14522     
14523     /**
14524      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14525      * @param {String} json The JSON string
14526      * @return {Object} The resulting object
14527      */
14528     this.decode = function(json){
14529         
14530         return  /** eval:var:json */ eval("(" + json + ')');
14531     };
14532 })();
14533 /** 
14534  * Shorthand for {@link Roo.util.JSON#encode}
14535  * @member Roo encode 
14536  * @method */
14537 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14538 /** 
14539  * Shorthand for {@link Roo.util.JSON#decode}
14540  * @member Roo decode 
14541  * @method */
14542 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14543 /*
14544  * Based on:
14545  * Ext JS Library 1.1.1
14546  * Copyright(c) 2006-2007, Ext JS, LLC.
14547  *
14548  * Originally Released Under LGPL - original licence link has changed is not relivant.
14549  *
14550  * Fork - LGPL
14551  * <script type="text/javascript">
14552  */
14553  
14554 /**
14555  * @class Roo.util.Format
14556  * Reusable data formatting functions
14557  * @static
14558  */
14559 Roo.util.Format = function(){
14560     var trimRe = /^\s+|\s+$/g;
14561     return {
14562         /**
14563          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14564          * @param {String} value The string to truncate
14565          * @param {Number} length The maximum length to allow before truncating
14566          * @return {String} The converted text
14567          */
14568         ellipsis : function(value, len){
14569             if(value && value.length > len){
14570                 return value.substr(0, len-3)+"...";
14571             }
14572             return value;
14573         },
14574
14575         /**
14576          * Checks a reference and converts it to empty string if it is undefined
14577          * @param {Mixed} value Reference to check
14578          * @return {Mixed} Empty string if converted, otherwise the original value
14579          */
14580         undef : function(value){
14581             return typeof value != "undefined" ? value : "";
14582         },
14583
14584         /**
14585          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14586          * @param {String} value The string to encode
14587          * @return {String} The encoded text
14588          */
14589         htmlEncode : function(value){
14590             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14591         },
14592
14593         /**
14594          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14595          * @param {String} value The string to decode
14596          * @return {String} The decoded text
14597          */
14598         htmlDecode : function(value){
14599             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14600         },
14601
14602         /**
14603          * Trims any whitespace from either side of a string
14604          * @param {String} value The text to trim
14605          * @return {String} The trimmed text
14606          */
14607         trim : function(value){
14608             return String(value).replace(trimRe, "");
14609         },
14610
14611         /**
14612          * Returns a substring from within an original string
14613          * @param {String} value The original text
14614          * @param {Number} start The start index of the substring
14615          * @param {Number} length The length of the substring
14616          * @return {String} The substring
14617          */
14618         substr : function(value, start, length){
14619             return String(value).substr(start, length);
14620         },
14621
14622         /**
14623          * Converts a string to all lower case letters
14624          * @param {String} value The text to convert
14625          * @return {String} The converted text
14626          */
14627         lowercase : function(value){
14628             return String(value).toLowerCase();
14629         },
14630
14631         /**
14632          * Converts a string to all upper case letters
14633          * @param {String} value The text to convert
14634          * @return {String} The converted text
14635          */
14636         uppercase : function(value){
14637             return String(value).toUpperCase();
14638         },
14639
14640         /**
14641          * Converts the first character only of a string to upper case
14642          * @param {String} value The text to convert
14643          * @return {String} The converted text
14644          */
14645         capitalize : function(value){
14646             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14647         },
14648
14649         // private
14650         call : function(value, fn){
14651             if(arguments.length > 2){
14652                 var args = Array.prototype.slice.call(arguments, 2);
14653                 args.unshift(value);
14654                  
14655                 return /** eval:var:value */  eval(fn).apply(window, args);
14656             }else{
14657                 /** eval:var:value */
14658                 return /** eval:var:value */ eval(fn).call(window, value);
14659             }
14660         },
14661
14662        
14663         /**
14664          * safer version of Math.toFixed..??/
14665          * @param {Number/String} value The numeric value to format
14666          * @param {Number/String} value Decimal places 
14667          * @return {String} The formatted currency string
14668          */
14669         toFixed : function(v, n)
14670         {
14671             // why not use to fixed - precision is buggered???
14672             if (!n) {
14673                 return Math.round(v-0);
14674             }
14675             var fact = Math.pow(10,n+1);
14676             v = (Math.round((v-0)*fact))/fact;
14677             var z = (''+fact).substring(2);
14678             if (v == Math.floor(v)) {
14679                 return Math.floor(v) + '.' + z;
14680             }
14681             
14682             // now just padd decimals..
14683             var ps = String(v).split('.');
14684             var fd = (ps[1] + z);
14685             var r = fd.substring(0,n); 
14686             var rm = fd.substring(n); 
14687             if (rm < 5) {
14688                 return ps[0] + '.' + r;
14689             }
14690             r*=1; // turn it into a number;
14691             r++;
14692             if (String(r).length != n) {
14693                 ps[0]*=1;
14694                 ps[0]++;
14695                 r = String(r).substring(1); // chop the end off.
14696             }
14697             
14698             return ps[0] + '.' + r;
14699              
14700         },
14701         
14702         /**
14703          * Format a number as US currency
14704          * @param {Number/String} value The numeric value to format
14705          * @return {String} The formatted currency string
14706          */
14707         usMoney : function(v){
14708             return '$' + Roo.util.Format.number(v);
14709         },
14710         
14711         /**
14712          * Format a number
14713          * eventually this should probably emulate php's number_format
14714          * @param {Number/String} value The numeric value to format
14715          * @param {Number} decimals number of decimal places
14716          * @param {String} delimiter for thousands (default comma)
14717          * @return {String} The formatted currency string
14718          */
14719         number : function(v, decimals, thousandsDelimiter)
14720         {
14721             // multiply and round.
14722             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14723             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14724             
14725             var mul = Math.pow(10, decimals);
14726             var zero = String(mul).substring(1);
14727             v = (Math.round((v-0)*mul))/mul;
14728             
14729             // if it's '0' number.. then
14730             
14731             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14732             v = String(v);
14733             var ps = v.split('.');
14734             var whole = ps[0];
14735             
14736             var r = /(\d+)(\d{3})/;
14737             // add comma's
14738             
14739             if(thousandsDelimiter.length != 0) {
14740                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14741             } 
14742             
14743             var sub = ps[1] ?
14744                     // has decimals..
14745                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14746                     // does not have decimals
14747                     (decimals ? ('.' + zero) : '');
14748             
14749             
14750             return whole + sub ;
14751         },
14752         
14753         /**
14754          * Parse a value into a formatted date using the specified format pattern.
14755          * @param {Mixed} value The value to format
14756          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14757          * @return {String} The formatted date string
14758          */
14759         date : function(v, format){
14760             if(!v){
14761                 return "";
14762             }
14763             if(!(v instanceof Date)){
14764                 v = new Date(Date.parse(v));
14765             }
14766             return v.dateFormat(format || Roo.util.Format.defaults.date);
14767         },
14768
14769         /**
14770          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14771          * @param {String} format Any valid date format string
14772          * @return {Function} The date formatting function
14773          */
14774         dateRenderer : function(format){
14775             return function(v){
14776                 return Roo.util.Format.date(v, format);  
14777             };
14778         },
14779
14780         // private
14781         stripTagsRE : /<\/?[^>]+>/gi,
14782         
14783         /**
14784          * Strips all HTML tags
14785          * @param {Mixed} value The text from which to strip tags
14786          * @return {String} The stripped text
14787          */
14788         stripTags : function(v){
14789             return !v ? v : String(v).replace(this.stripTagsRE, "");
14790         },
14791         
14792         /**
14793          * Size in Mb,Gb etc.
14794          * @param {Number} value The number to be formated
14795          * @param {number} decimals how many decimal places
14796          * @return {String} the formated string
14797          */
14798         size : function(value, decimals)
14799         {
14800             var sizes = ['b', 'k', 'M', 'G', 'T'];
14801             if (value == 0) {
14802                 return 0;
14803             }
14804             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14805             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14806         }
14807         
14808         
14809         
14810     };
14811 }();
14812 Roo.util.Format.defaults = {
14813     date : 'd/M/Y'
14814 };/*
14815  * Based on:
14816  * Ext JS Library 1.1.1
14817  * Copyright(c) 2006-2007, Ext JS, LLC.
14818  *
14819  * Originally Released Under LGPL - original licence link has changed is not relivant.
14820  *
14821  * Fork - LGPL
14822  * <script type="text/javascript">
14823  */
14824
14825
14826  
14827
14828 /**
14829  * @class Roo.MasterTemplate
14830  * @extends Roo.Template
14831  * Provides a template that can have child templates. The syntax is:
14832 <pre><code>
14833 var t = new Roo.MasterTemplate(
14834         '&lt;select name="{name}"&gt;',
14835                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14836         '&lt;/select&gt;'
14837 );
14838 t.add('options', {value: 'foo', text: 'bar'});
14839 // or you can add multiple child elements in one shot
14840 t.addAll('options', [
14841     {value: 'foo', text: 'bar'},
14842     {value: 'foo2', text: 'bar2'},
14843     {value: 'foo3', text: 'bar3'}
14844 ]);
14845 // then append, applying the master template values
14846 t.append('my-form', {name: 'my-select'});
14847 </code></pre>
14848 * A name attribute for the child template is not required if you have only one child
14849 * template or you want to refer to them by index.
14850  */
14851 Roo.MasterTemplate = function(){
14852     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14853     this.originalHtml = this.html;
14854     var st = {};
14855     var m, re = this.subTemplateRe;
14856     re.lastIndex = 0;
14857     var subIndex = 0;
14858     while(m = re.exec(this.html)){
14859         var name = m[1], content = m[2];
14860         st[subIndex] = {
14861             name: name,
14862             index: subIndex,
14863             buffer: [],
14864             tpl : new Roo.Template(content)
14865         };
14866         if(name){
14867             st[name] = st[subIndex];
14868         }
14869         st[subIndex].tpl.compile();
14870         st[subIndex].tpl.call = this.call.createDelegate(this);
14871         subIndex++;
14872     }
14873     this.subCount = subIndex;
14874     this.subs = st;
14875 };
14876 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14877     /**
14878     * The regular expression used to match sub templates
14879     * @type RegExp
14880     * @property
14881     */
14882     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14883
14884     /**
14885      * Applies the passed values to a child template.
14886      * @param {String/Number} name (optional) The name or index of the child template
14887      * @param {Array/Object} values The values to be applied to the template
14888      * @return {MasterTemplate} this
14889      */
14890      add : function(name, values){
14891         if(arguments.length == 1){
14892             values = arguments[0];
14893             name = 0;
14894         }
14895         var s = this.subs[name];
14896         s.buffer[s.buffer.length] = s.tpl.apply(values);
14897         return this;
14898     },
14899
14900     /**
14901      * Applies all the passed values to a child template.
14902      * @param {String/Number} name (optional) The name or index of the child template
14903      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14904      * @param {Boolean} reset (optional) True to reset the template first
14905      * @return {MasterTemplate} this
14906      */
14907     fill : function(name, values, reset){
14908         var a = arguments;
14909         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14910             values = a[0];
14911             name = 0;
14912             reset = a[1];
14913         }
14914         if(reset){
14915             this.reset();
14916         }
14917         for(var i = 0, len = values.length; i < len; i++){
14918             this.add(name, values[i]);
14919         }
14920         return this;
14921     },
14922
14923     /**
14924      * Resets the template for reuse
14925      * @return {MasterTemplate} this
14926      */
14927      reset : function(){
14928         var s = this.subs;
14929         for(var i = 0; i < this.subCount; i++){
14930             s[i].buffer = [];
14931         }
14932         return this;
14933     },
14934
14935     applyTemplate : function(values){
14936         var s = this.subs;
14937         var replaceIndex = -1;
14938         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14939             return s[++replaceIndex].buffer.join("");
14940         });
14941         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14942     },
14943
14944     apply : function(){
14945         return this.applyTemplate.apply(this, arguments);
14946     },
14947
14948     compile : function(){return this;}
14949 });
14950
14951 /**
14952  * Alias for fill().
14953  * @method
14954  */
14955 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14956  /**
14957  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14958  * var tpl = Roo.MasterTemplate.from('element-id');
14959  * @param {String/HTMLElement} el
14960  * @param {Object} config
14961  * @static
14962  */
14963 Roo.MasterTemplate.from = function(el, config){
14964     el = Roo.getDom(el);
14965     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14966 };/*
14967  * Based on:
14968  * Ext JS Library 1.1.1
14969  * Copyright(c) 2006-2007, Ext JS, LLC.
14970  *
14971  * Originally Released Under LGPL - original licence link has changed is not relivant.
14972  *
14973  * Fork - LGPL
14974  * <script type="text/javascript">
14975  */
14976
14977  
14978 /**
14979  * @class Roo.util.CSS
14980  * Utility class for manipulating CSS rules
14981  * @static
14982
14983  */
14984 Roo.util.CSS = function(){
14985         var rules = null;
14986         var doc = document;
14987
14988     var camelRe = /(-[a-z])/gi;
14989     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14990
14991    return {
14992    /**
14993     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14994     * tag and appended to the HEAD of the document.
14995     * @param {String|Object} cssText The text containing the css rules
14996     * @param {String} id An id to add to the stylesheet for later removal
14997     * @return {StyleSheet}
14998     */
14999     createStyleSheet : function(cssText, id){
15000         var ss;
15001         var head = doc.getElementsByTagName("head")[0];
15002         var nrules = doc.createElement("style");
15003         nrules.setAttribute("type", "text/css");
15004         if(id){
15005             nrules.setAttribute("id", id);
15006         }
15007         if (typeof(cssText) != 'string') {
15008             // support object maps..
15009             // not sure if this a good idea.. 
15010             // perhaps it should be merged with the general css handling
15011             // and handle js style props.
15012             var cssTextNew = [];
15013             for(var n in cssText) {
15014                 var citems = [];
15015                 for(var k in cssText[n]) {
15016                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15017                 }
15018                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15019                 
15020             }
15021             cssText = cssTextNew.join("\n");
15022             
15023         }
15024        
15025        
15026        if(Roo.isIE){
15027            head.appendChild(nrules);
15028            ss = nrules.styleSheet;
15029            ss.cssText = cssText;
15030        }else{
15031            try{
15032                 nrules.appendChild(doc.createTextNode(cssText));
15033            }catch(e){
15034                nrules.cssText = cssText; 
15035            }
15036            head.appendChild(nrules);
15037            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15038        }
15039        this.cacheStyleSheet(ss);
15040        return ss;
15041    },
15042
15043    /**
15044     * Removes a style or link tag by id
15045     * @param {String} id The id of the tag
15046     */
15047    removeStyleSheet : function(id){
15048        var existing = doc.getElementById(id);
15049        if(existing){
15050            existing.parentNode.removeChild(existing);
15051        }
15052    },
15053
15054    /**
15055     * Dynamically swaps an existing stylesheet reference for a new one
15056     * @param {String} id The id of an existing link tag to remove
15057     * @param {String} url The href of the new stylesheet to include
15058     */
15059    swapStyleSheet : function(id, url){
15060        this.removeStyleSheet(id);
15061        var ss = doc.createElement("link");
15062        ss.setAttribute("rel", "stylesheet");
15063        ss.setAttribute("type", "text/css");
15064        ss.setAttribute("id", id);
15065        ss.setAttribute("href", url);
15066        doc.getElementsByTagName("head")[0].appendChild(ss);
15067    },
15068    
15069    /**
15070     * Refresh the rule cache if you have dynamically added stylesheets
15071     * @return {Object} An object (hash) of rules indexed by selector
15072     */
15073    refreshCache : function(){
15074        return this.getRules(true);
15075    },
15076
15077    // private
15078    cacheStyleSheet : function(stylesheet){
15079        if(!rules){
15080            rules = {};
15081        }
15082        try{// try catch for cross domain access issue
15083            var ssRules = stylesheet.cssRules || stylesheet.rules;
15084            for(var j = ssRules.length-1; j >= 0; --j){
15085                rules[ssRules[j].selectorText] = ssRules[j];
15086            }
15087        }catch(e){}
15088    },
15089    
15090    /**
15091     * Gets all css rules for the document
15092     * @param {Boolean} refreshCache true to refresh the internal cache
15093     * @return {Object} An object (hash) of rules indexed by selector
15094     */
15095    getRules : function(refreshCache){
15096                 if(rules == null || refreshCache){
15097                         rules = {};
15098                         var ds = doc.styleSheets;
15099                         for(var i =0, len = ds.length; i < len; i++){
15100                             try{
15101                         this.cacheStyleSheet(ds[i]);
15102                     }catch(e){} 
15103                 }
15104                 }
15105                 return rules;
15106         },
15107         
15108         /**
15109     * Gets an an individual CSS rule by selector(s)
15110     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15111     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15112     * @return {CSSRule} The CSS rule or null if one is not found
15113     */
15114    getRule : function(selector, refreshCache){
15115                 var rs = this.getRules(refreshCache);
15116                 if(!(selector instanceof Array)){
15117                     return rs[selector];
15118                 }
15119                 for(var i = 0; i < selector.length; i++){
15120                         if(rs[selector[i]]){
15121                                 return rs[selector[i]];
15122                         }
15123                 }
15124                 return null;
15125         },
15126         
15127         
15128         /**
15129     * Updates a rule property
15130     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15131     * @param {String} property The css property
15132     * @param {String} value The new value for the property
15133     * @return {Boolean} true If a rule was found and updated
15134     */
15135    updateRule : function(selector, property, value){
15136                 if(!(selector instanceof Array)){
15137                         var rule = this.getRule(selector);
15138                         if(rule){
15139                                 rule.style[property.replace(camelRe, camelFn)] = value;
15140                                 return true;
15141                         }
15142                 }else{
15143                         for(var i = 0; i < selector.length; i++){
15144                                 if(this.updateRule(selector[i], property, value)){
15145                                         return true;
15146                                 }
15147                         }
15148                 }
15149                 return false;
15150         }
15151    };   
15152 }();/*
15153  * Based on:
15154  * Ext JS Library 1.1.1
15155  * Copyright(c) 2006-2007, Ext JS, LLC.
15156  *
15157  * Originally Released Under LGPL - original licence link has changed is not relivant.
15158  *
15159  * Fork - LGPL
15160  * <script type="text/javascript">
15161  */
15162
15163  
15164
15165 /**
15166  * @class Roo.util.ClickRepeater
15167  * @extends Roo.util.Observable
15168  * 
15169  * A wrapper class which can be applied to any element. Fires a "click" event while the
15170  * mouse is pressed. The interval between firings may be specified in the config but
15171  * defaults to 10 milliseconds.
15172  * 
15173  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15174  * 
15175  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15176  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15177  * Similar to an autorepeat key delay.
15178  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15179  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15180  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15181  *           "interval" and "delay" are ignored. "immediate" is honored.
15182  * @cfg {Boolean} preventDefault True to prevent the default click event
15183  * @cfg {Boolean} stopDefault True to stop the default click event
15184  * 
15185  * @history
15186  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15187  *     2007-02-02 jvs Renamed to ClickRepeater
15188  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15189  *
15190  *  @constructor
15191  * @param {String/HTMLElement/Element} el The element to listen on
15192  * @param {Object} config
15193  **/
15194 Roo.util.ClickRepeater = function(el, config)
15195 {
15196     this.el = Roo.get(el);
15197     this.el.unselectable();
15198
15199     Roo.apply(this, config);
15200
15201     this.addEvents({
15202     /**
15203      * @event mousedown
15204      * Fires when the mouse button is depressed.
15205      * @param {Roo.util.ClickRepeater} this
15206      */
15207         "mousedown" : true,
15208     /**
15209      * @event click
15210      * Fires on a specified interval during the time the element is pressed.
15211      * @param {Roo.util.ClickRepeater} this
15212      */
15213         "click" : true,
15214     /**
15215      * @event mouseup
15216      * Fires when the mouse key is released.
15217      * @param {Roo.util.ClickRepeater} this
15218      */
15219         "mouseup" : true
15220     });
15221
15222     this.el.on("mousedown", this.handleMouseDown, this);
15223     if(this.preventDefault || this.stopDefault){
15224         this.el.on("click", function(e){
15225             if(this.preventDefault){
15226                 e.preventDefault();
15227             }
15228             if(this.stopDefault){
15229                 e.stopEvent();
15230             }
15231         }, this);
15232     }
15233
15234     // allow inline handler
15235     if(this.handler){
15236         this.on("click", this.handler,  this.scope || this);
15237     }
15238
15239     Roo.util.ClickRepeater.superclass.constructor.call(this);
15240 };
15241
15242 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15243     interval : 20,
15244     delay: 250,
15245     preventDefault : true,
15246     stopDefault : false,
15247     timer : 0,
15248
15249     // private
15250     handleMouseDown : function(){
15251         clearTimeout(this.timer);
15252         this.el.blur();
15253         if(this.pressClass){
15254             this.el.addClass(this.pressClass);
15255         }
15256         this.mousedownTime = new Date();
15257
15258         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15259         this.el.on("mouseout", this.handleMouseOut, this);
15260
15261         this.fireEvent("mousedown", this);
15262         this.fireEvent("click", this);
15263         
15264         this.timer = this.click.defer(this.delay || this.interval, this);
15265     },
15266
15267     // private
15268     click : function(){
15269         this.fireEvent("click", this);
15270         this.timer = this.click.defer(this.getInterval(), this);
15271     },
15272
15273     // private
15274     getInterval: function(){
15275         if(!this.accelerate){
15276             return this.interval;
15277         }
15278         var pressTime = this.mousedownTime.getElapsed();
15279         if(pressTime < 500){
15280             return 400;
15281         }else if(pressTime < 1700){
15282             return 320;
15283         }else if(pressTime < 2600){
15284             return 250;
15285         }else if(pressTime < 3500){
15286             return 180;
15287         }else if(pressTime < 4400){
15288             return 140;
15289         }else if(pressTime < 5300){
15290             return 80;
15291         }else if(pressTime < 6200){
15292             return 50;
15293         }else{
15294             return 10;
15295         }
15296     },
15297
15298     // private
15299     handleMouseOut : function(){
15300         clearTimeout(this.timer);
15301         if(this.pressClass){
15302             this.el.removeClass(this.pressClass);
15303         }
15304         this.el.on("mouseover", this.handleMouseReturn, this);
15305     },
15306
15307     // private
15308     handleMouseReturn : function(){
15309         this.el.un("mouseover", this.handleMouseReturn);
15310         if(this.pressClass){
15311             this.el.addClass(this.pressClass);
15312         }
15313         this.click();
15314     },
15315
15316     // private
15317     handleMouseUp : function(){
15318         clearTimeout(this.timer);
15319         this.el.un("mouseover", this.handleMouseReturn);
15320         this.el.un("mouseout", this.handleMouseOut);
15321         Roo.get(document).un("mouseup", this.handleMouseUp);
15322         this.el.removeClass(this.pressClass);
15323         this.fireEvent("mouseup", this);
15324     }
15325 });/**
15326  * @class Roo.util.Clipboard
15327  * @static
15328  * 
15329  * Clipboard UTILS
15330  * 
15331  **/
15332 Roo.util.Clipboard = {
15333     /**
15334      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15335      * @param {String} text to copy to clipboard
15336      */
15337     write : function(text) {
15338         // navigator clipboard api needs a secure context (https)
15339         if (navigator.clipboard && window.isSecureContext) {
15340             // navigator clipboard api method'
15341             navigator.clipboard.writeText(text);
15342             return ;
15343         } 
15344         // text area method
15345         var ta = document.createElement("textarea");
15346         ta.value = text;
15347         // make the textarea out of viewport
15348         ta.style.position = "fixed";
15349         ta.style.left = "-999999px";
15350         ta.style.top = "-999999px";
15351         document.body.appendChild(ta);
15352         ta.focus();
15353         ta.select();
15354         document.execCommand('copy');
15355         (function() {
15356             ta.remove();
15357         }).defer(100);
15358         
15359     }
15360         
15361 }
15362     /*
15363  * Based on:
15364  * Ext JS Library 1.1.1
15365  * Copyright(c) 2006-2007, Ext JS, LLC.
15366  *
15367  * Originally Released Under LGPL - original licence link has changed is not relivant.
15368  *
15369  * Fork - LGPL
15370  * <script type="text/javascript">
15371  */
15372
15373  
15374 /**
15375  * @class Roo.KeyNav
15376  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15377  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15378  * way to implement custom navigation schemes for any UI component.</p>
15379  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15380  * pageUp, pageDown, del, home, end.  Usage:</p>
15381  <pre><code>
15382 var nav = new Roo.KeyNav("my-element", {
15383     "left" : function(e){
15384         this.moveLeft(e.ctrlKey);
15385     },
15386     "right" : function(e){
15387         this.moveRight(e.ctrlKey);
15388     },
15389     "enter" : function(e){
15390         this.save();
15391     },
15392     scope : this
15393 });
15394 </code></pre>
15395  * @constructor
15396  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15397  * @param {Object} config The config
15398  */
15399 Roo.KeyNav = function(el, config){
15400     this.el = Roo.get(el);
15401     Roo.apply(this, config);
15402     if(!this.disabled){
15403         this.disabled = true;
15404         this.enable();
15405     }
15406 };
15407
15408 Roo.KeyNav.prototype = {
15409     /**
15410      * @cfg {Boolean} disabled
15411      * True to disable this KeyNav instance (defaults to false)
15412      */
15413     disabled : false,
15414     /**
15415      * @cfg {String} defaultEventAction
15416      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15417      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15418      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15419      */
15420     defaultEventAction: "stopEvent",
15421     /**
15422      * @cfg {Boolean} forceKeyDown
15423      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15424      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15425      * handle keydown instead of keypress.
15426      */
15427     forceKeyDown : false,
15428
15429     // private
15430     prepareEvent : function(e){
15431         var k = e.getKey();
15432         var h = this.keyToHandler[k];
15433         //if(h && this[h]){
15434         //    e.stopPropagation();
15435         //}
15436         if(Roo.isSafari && h && k >= 37 && k <= 40){
15437             e.stopEvent();
15438         }
15439     },
15440
15441     // private
15442     relay : function(e){
15443         var k = e.getKey();
15444         var h = this.keyToHandler[k];
15445         if(h && this[h]){
15446             if(this.doRelay(e, this[h], h) !== true){
15447                 e[this.defaultEventAction]();
15448             }
15449         }
15450     },
15451
15452     // private
15453     doRelay : function(e, h, hname){
15454         return h.call(this.scope || this, e);
15455     },
15456
15457     // possible handlers
15458     enter : false,
15459     left : false,
15460     right : false,
15461     up : false,
15462     down : false,
15463     tab : false,
15464     esc : false,
15465     pageUp : false,
15466     pageDown : false,
15467     del : false,
15468     home : false,
15469     end : false,
15470
15471     // quick lookup hash
15472     keyToHandler : {
15473         37 : "left",
15474         39 : "right",
15475         38 : "up",
15476         40 : "down",
15477         33 : "pageUp",
15478         34 : "pageDown",
15479         46 : "del",
15480         36 : "home",
15481         35 : "end",
15482         13 : "enter",
15483         27 : "esc",
15484         9  : "tab"
15485     },
15486
15487         /**
15488          * Enable this KeyNav
15489          */
15490         enable: function(){
15491                 if(this.disabled){
15492             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15493             // the EventObject will normalize Safari automatically
15494             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15495                 this.el.on("keydown", this.relay,  this);
15496             }else{
15497                 this.el.on("keydown", this.prepareEvent,  this);
15498                 this.el.on("keypress", this.relay,  this);
15499             }
15500                     this.disabled = false;
15501                 }
15502         },
15503
15504         /**
15505          * Disable this KeyNav
15506          */
15507         disable: function(){
15508                 if(!this.disabled){
15509                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15510                 this.el.un("keydown", this.relay);
15511             }else{
15512                 this.el.un("keydown", this.prepareEvent);
15513                 this.el.un("keypress", this.relay);
15514             }
15515                     this.disabled = true;
15516                 }
15517         }
15518 };/*
15519  * Based on:
15520  * Ext JS Library 1.1.1
15521  * Copyright(c) 2006-2007, Ext JS, LLC.
15522  *
15523  * Originally Released Under LGPL - original licence link has changed is not relivant.
15524  *
15525  * Fork - LGPL
15526  * <script type="text/javascript">
15527  */
15528
15529  
15530 /**
15531  * @class Roo.KeyMap
15532  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15533  * The constructor accepts the same config object as defined by {@link #addBinding}.
15534  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15535  * combination it will call the function with this signature (if the match is a multi-key
15536  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15537  * A KeyMap can also handle a string representation of keys.<br />
15538  * Usage:
15539  <pre><code>
15540 // map one key by key code
15541 var map = new Roo.KeyMap("my-element", {
15542     key: 13, // or Roo.EventObject.ENTER
15543     fn: myHandler,
15544     scope: myObject
15545 });
15546
15547 // map multiple keys to one action by string
15548 var map = new Roo.KeyMap("my-element", {
15549     key: "a\r\n\t",
15550     fn: myHandler,
15551     scope: myObject
15552 });
15553
15554 // map multiple keys to multiple actions by strings and array of codes
15555 var map = new Roo.KeyMap("my-element", [
15556     {
15557         key: [10,13],
15558         fn: function(){ alert("Return was pressed"); }
15559     }, {
15560         key: "abc",
15561         fn: function(){ alert('a, b or c was pressed'); }
15562     }, {
15563         key: "\t",
15564         ctrl:true,
15565         shift:true,
15566         fn: function(){ alert('Control + shift + tab was pressed.'); }
15567     }
15568 ]);
15569 </code></pre>
15570  * <b>Note: A KeyMap starts enabled</b>
15571  * @constructor
15572  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15573  * @param {Object} config The config (see {@link #addBinding})
15574  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15575  */
15576 Roo.KeyMap = function(el, config, eventName){
15577     this.el  = Roo.get(el);
15578     this.eventName = eventName || "keydown";
15579     this.bindings = [];
15580     if(config){
15581         this.addBinding(config);
15582     }
15583     this.enable();
15584 };
15585
15586 Roo.KeyMap.prototype = {
15587     /**
15588      * True to stop the event from bubbling and prevent the default browser action if the
15589      * key was handled by the KeyMap (defaults to false)
15590      * @type Boolean
15591      */
15592     stopEvent : false,
15593
15594     /**
15595      * Add a new binding to this KeyMap. The following config object properties are supported:
15596      * <pre>
15597 Property    Type             Description
15598 ----------  ---------------  ----------------------------------------------------------------------
15599 key         String/Array     A single keycode or an array of keycodes to handle
15600 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15601 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15602 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15603 fn          Function         The function to call when KeyMap finds the expected key combination
15604 scope       Object           The scope of the callback function
15605 </pre>
15606      *
15607      * Usage:
15608      * <pre><code>
15609 // Create a KeyMap
15610 var map = new Roo.KeyMap(document, {
15611     key: Roo.EventObject.ENTER,
15612     fn: handleKey,
15613     scope: this
15614 });
15615
15616 //Add a new binding to the existing KeyMap later
15617 map.addBinding({
15618     key: 'abc',
15619     shift: true,
15620     fn: handleKey,
15621     scope: this
15622 });
15623 </code></pre>
15624      * @param {Object/Array} config A single KeyMap config or an array of configs
15625      */
15626         addBinding : function(config){
15627         if(config instanceof Array){
15628             for(var i = 0, len = config.length; i < len; i++){
15629                 this.addBinding(config[i]);
15630             }
15631             return;
15632         }
15633         var keyCode = config.key,
15634             shift = config.shift, 
15635             ctrl = config.ctrl, 
15636             alt = config.alt,
15637             fn = config.fn,
15638             scope = config.scope;
15639         if(typeof keyCode == "string"){
15640             var ks = [];
15641             var keyString = keyCode.toUpperCase();
15642             for(var j = 0, len = keyString.length; j < len; j++){
15643                 ks.push(keyString.charCodeAt(j));
15644             }
15645             keyCode = ks;
15646         }
15647         var keyArray = keyCode instanceof Array;
15648         var handler = function(e){
15649             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15650                 var k = e.getKey();
15651                 if(keyArray){
15652                     for(var i = 0, len = keyCode.length; i < len; i++){
15653                         if(keyCode[i] == k){
15654                           if(this.stopEvent){
15655                               e.stopEvent();
15656                           }
15657                           fn.call(scope || window, k, e);
15658                           return;
15659                         }
15660                     }
15661                 }else{
15662                     if(k == keyCode){
15663                         if(this.stopEvent){
15664                            e.stopEvent();
15665                         }
15666                         fn.call(scope || window, k, e);
15667                     }
15668                 }
15669             }
15670         };
15671         this.bindings.push(handler);  
15672         },
15673
15674     /**
15675      * Shorthand for adding a single key listener
15676      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15677      * following options:
15678      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15679      * @param {Function} fn The function to call
15680      * @param {Object} scope (optional) The scope of the function
15681      */
15682     on : function(key, fn, scope){
15683         var keyCode, shift, ctrl, alt;
15684         if(typeof key == "object" && !(key instanceof Array)){
15685             keyCode = key.key;
15686             shift = key.shift;
15687             ctrl = key.ctrl;
15688             alt = key.alt;
15689         }else{
15690             keyCode = key;
15691         }
15692         this.addBinding({
15693             key: keyCode,
15694             shift: shift,
15695             ctrl: ctrl,
15696             alt: alt,
15697             fn: fn,
15698             scope: scope
15699         })
15700     },
15701
15702     // private
15703     handleKeyDown : function(e){
15704             if(this.enabled){ //just in case
15705             var b = this.bindings;
15706             for(var i = 0, len = b.length; i < len; i++){
15707                 b[i].call(this, e);
15708             }
15709             }
15710         },
15711         
15712         /**
15713          * Returns true if this KeyMap is enabled
15714          * @return {Boolean} 
15715          */
15716         isEnabled : function(){
15717             return this.enabled;  
15718         },
15719         
15720         /**
15721          * Enables this KeyMap
15722          */
15723         enable: function(){
15724                 if(!this.enabled){
15725                     this.el.on(this.eventName, this.handleKeyDown, this);
15726                     this.enabled = true;
15727                 }
15728         },
15729
15730         /**
15731          * Disable this KeyMap
15732          */
15733         disable: function(){
15734                 if(this.enabled){
15735                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15736                     this.enabled = false;
15737                 }
15738         }
15739 };/*
15740  * Based on:
15741  * Ext JS Library 1.1.1
15742  * Copyright(c) 2006-2007, Ext JS, LLC.
15743  *
15744  * Originally Released Under LGPL - original licence link has changed is not relivant.
15745  *
15746  * Fork - LGPL
15747  * <script type="text/javascript">
15748  */
15749
15750  
15751 /**
15752  * @class Roo.util.TextMetrics
15753  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15754  * wide, in pixels, a given block of text will be.
15755  * @static
15756  */
15757 Roo.util.TextMetrics = function(){
15758     var shared;
15759     return {
15760         /**
15761          * Measures the size of the specified text
15762          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15763          * that can affect the size of the rendered text
15764          * @param {String} text The text to measure
15765          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15766          * in order to accurately measure the text height
15767          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15768          */
15769         measure : function(el, text, fixedWidth){
15770             if(!shared){
15771                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15772             }
15773             shared.bind(el);
15774             shared.setFixedWidth(fixedWidth || 'auto');
15775             return shared.getSize(text);
15776         },
15777
15778         /**
15779          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15780          * the overhead of multiple calls to initialize the style properties on each measurement.
15781          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15782          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15783          * in order to accurately measure the text height
15784          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15785          */
15786         createInstance : function(el, fixedWidth){
15787             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15788         }
15789     };
15790 }();
15791
15792 /**
15793  * @class Roo.util.TextMetrics.Instance
15794  * Instance of  TextMetrics Calcuation
15795  * @constructor
15796  * Create a new TextMetrics Instance
15797  * @param {Object} bindto
15798  * @param {Boolean} fixedWidth
15799  */
15800
15801 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15802 {
15803     var ml = new Roo.Element(document.createElement('div'));
15804     document.body.appendChild(ml.dom);
15805     ml.position('absolute');
15806     ml.setLeftTop(-1000, -1000);
15807     ml.hide();
15808
15809     if(fixedWidth){
15810         ml.setWidth(fixedWidth);
15811     }
15812      
15813     var instance = {
15814         /**
15815          * Returns the size of the specified text based on the internal element's style and width properties
15816          * @param {String} text The text to measure
15817          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15818          */
15819         getSize : function(text){
15820             ml.update(text);
15821             var s = ml.getSize();
15822             ml.update('');
15823             return s;
15824         },
15825
15826         /**
15827          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15828          * that can affect the size of the rendered text
15829          * @param {String/HTMLElement} el The element, dom node or id
15830          */
15831         bind : function(el){
15832             ml.setStyle(
15833                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15834             );
15835         },
15836
15837         /**
15838          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15839          * to set a fixed width in order to accurately measure the text height.
15840          * @param {Number} width The width to set on the element
15841          */
15842         setFixedWidth : function(width){
15843             ml.setWidth(width);
15844         },
15845
15846         /**
15847          * Returns the measured width of the specified text
15848          * @param {String} text The text to measure
15849          * @return {Number} width The width in pixels
15850          */
15851         getWidth : function(text){
15852             ml.dom.style.width = 'auto';
15853             return this.getSize(text).width;
15854         },
15855
15856         /**
15857          * Returns the measured height of the specified text.  For multiline text, be sure to call
15858          * {@link #setFixedWidth} if necessary.
15859          * @param {String} text The text to measure
15860          * @return {Number} height The height in pixels
15861          */
15862         getHeight : function(text){
15863             return this.getSize(text).height;
15864         }
15865     };
15866
15867     instance.bind(bindTo);
15868
15869     return instance;
15870 };
15871
15872 // backwards compat
15873 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15874  * Based on:
15875  * Ext JS Library 1.1.1
15876  * Copyright(c) 2006-2007, Ext JS, LLC.
15877  *
15878  * Originally Released Under LGPL - original licence link has changed is not relivant.
15879  *
15880  * Fork - LGPL
15881  * <script type="text/javascript">
15882  */
15883
15884 /**
15885  * @class Roo.state.Provider
15886  * Abstract base class for state provider implementations. This class provides methods
15887  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15888  * Provider interface.
15889  */
15890 Roo.state.Provider = function(){
15891     /**
15892      * @event statechange
15893      * Fires when a state change occurs.
15894      * @param {Provider} this This state provider
15895      * @param {String} key The state key which was changed
15896      * @param {String} value The encoded value for the state
15897      */
15898     this.addEvents({
15899         "statechange": true
15900     });
15901     this.state = {};
15902     Roo.state.Provider.superclass.constructor.call(this);
15903 };
15904 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15905     /**
15906      * Returns the current value for a key
15907      * @param {String} name The key name
15908      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15909      * @return {Mixed} The state data
15910      */
15911     get : function(name, defaultValue){
15912         return typeof this.state[name] == "undefined" ?
15913             defaultValue : this.state[name];
15914     },
15915     
15916     /**
15917      * Clears a value from the state
15918      * @param {String} name The key name
15919      */
15920     clear : function(name){
15921         delete this.state[name];
15922         this.fireEvent("statechange", this, name, null);
15923     },
15924     
15925     /**
15926      * Sets the value for a key
15927      * @param {String} name The key name
15928      * @param {Mixed} value The value to set
15929      */
15930     set : function(name, value){
15931         this.state[name] = value;
15932         this.fireEvent("statechange", this, name, value);
15933     },
15934     
15935     /**
15936      * Decodes a string previously encoded with {@link #encodeValue}.
15937      * @param {String} value The value to decode
15938      * @return {Mixed} The decoded value
15939      */
15940     decodeValue : function(cookie){
15941         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15942         var matches = re.exec(unescape(cookie));
15943         if(!matches || !matches[1]) {
15944             return; // non state cookie
15945         }
15946         var type = matches[1];
15947         var v = matches[2];
15948         switch(type){
15949             case "n":
15950                 return parseFloat(v);
15951             case "d":
15952                 return new Date(Date.parse(v));
15953             case "b":
15954                 return (v == "1");
15955             case "a":
15956                 var all = [];
15957                 var values = v.split("^");
15958                 for(var i = 0, len = values.length; i < len; i++){
15959                     all.push(this.decodeValue(values[i]));
15960                 }
15961                 return all;
15962            case "o":
15963                 var all = {};
15964                 var values = v.split("^");
15965                 for(var i = 0, len = values.length; i < len; i++){
15966                     var kv = values[i].split("=");
15967                     all[kv[0]] = this.decodeValue(kv[1]);
15968                 }
15969                 return all;
15970            default:
15971                 return v;
15972         }
15973     },
15974     
15975     /**
15976      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15977      * @param {Mixed} value The value to encode
15978      * @return {String} The encoded value
15979      */
15980     encodeValue : function(v){
15981         var enc;
15982         if(typeof v == "number"){
15983             enc = "n:" + v;
15984         }else if(typeof v == "boolean"){
15985             enc = "b:" + (v ? "1" : "0");
15986         }else if(v instanceof Date){
15987             enc = "d:" + v.toGMTString();
15988         }else if(v instanceof Array){
15989             var flat = "";
15990             for(var i = 0, len = v.length; i < len; i++){
15991                 flat += this.encodeValue(v[i]);
15992                 if(i != len-1) {
15993                     flat += "^";
15994                 }
15995             }
15996             enc = "a:" + flat;
15997         }else if(typeof v == "object"){
15998             var flat = "";
15999             for(var key in v){
16000                 if(typeof v[key] != "function"){
16001                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16002                 }
16003             }
16004             enc = "o:" + flat.substring(0, flat.length-1);
16005         }else{
16006             enc = "s:" + v;
16007         }
16008         return escape(enc);        
16009     }
16010 });
16011
16012 /*
16013  * Based on:
16014  * Ext JS Library 1.1.1
16015  * Copyright(c) 2006-2007, Ext JS, LLC.
16016  *
16017  * Originally Released Under LGPL - original licence link has changed is not relivant.
16018  *
16019  * Fork - LGPL
16020  * <script type="text/javascript">
16021  */
16022 /**
16023  * @class Roo.state.Manager
16024  * This is the global state manager. By default all components that are "state aware" check this class
16025  * for state information if you don't pass them a custom state provider. In order for this class
16026  * to be useful, it must be initialized with a provider when your application initializes.
16027  <pre><code>
16028 // in your initialization function
16029 init : function(){
16030    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16031    ...
16032    // supposed you have a {@link Roo.BorderLayout}
16033    var layout = new Roo.BorderLayout(...);
16034    layout.restoreState();
16035    // or a {Roo.BasicDialog}
16036    var dialog = new Roo.BasicDialog(...);
16037    dialog.restoreState();
16038  </code></pre>
16039  * @static
16040  */
16041 Roo.state.Manager = function(){
16042     var provider = new Roo.state.Provider();
16043     
16044     return {
16045         /**
16046          * Configures the default state provider for your application
16047          * @param {Provider} stateProvider The state provider to set
16048          */
16049         setProvider : function(stateProvider){
16050             provider = stateProvider;
16051         },
16052         
16053         /**
16054          * Returns the current value for a key
16055          * @param {String} name The key name
16056          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16057          * @return {Mixed} The state data
16058          */
16059         get : function(key, defaultValue){
16060             return provider.get(key, defaultValue);
16061         },
16062         
16063         /**
16064          * Sets the value for a key
16065          * @param {String} name The key name
16066          * @param {Mixed} value The state data
16067          */
16068          set : function(key, value){
16069             provider.set(key, value);
16070         },
16071         
16072         /**
16073          * Clears a value from the state
16074          * @param {String} name The key name
16075          */
16076         clear : function(key){
16077             provider.clear(key);
16078         },
16079         
16080         /**
16081          * Gets the currently configured state provider
16082          * @return {Provider} The state provider
16083          */
16084         getProvider : function(){
16085             return provider;
16086         }
16087     };
16088 }();
16089 /*
16090  * Based on:
16091  * Ext JS Library 1.1.1
16092  * Copyright(c) 2006-2007, Ext JS, LLC.
16093  *
16094  * Originally Released Under LGPL - original licence link has changed is not relivant.
16095  *
16096  * Fork - LGPL
16097  * <script type="text/javascript">
16098  */
16099 /**
16100  * @class Roo.state.CookieProvider
16101  * @extends Roo.state.Provider
16102  * The default Provider implementation which saves state via cookies.
16103  * <br />Usage:
16104  <pre><code>
16105    var cp = new Roo.state.CookieProvider({
16106        path: "/cgi-bin/",
16107        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16108        domain: "roojs.com"
16109    })
16110    Roo.state.Manager.setProvider(cp);
16111  </code></pre>
16112  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16113  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16114  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16115  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16116  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16117  * domain the page is running on including the 'www' like 'www.roojs.com')
16118  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16119  * @constructor
16120  * Create a new CookieProvider
16121  * @param {Object} config The configuration object
16122  */
16123 Roo.state.CookieProvider = function(config){
16124     Roo.state.CookieProvider.superclass.constructor.call(this);
16125     this.path = "/";
16126     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16127     this.domain = null;
16128     this.secure = false;
16129     Roo.apply(this, config);
16130     this.state = this.readCookies();
16131 };
16132
16133 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16134     // private
16135     set : function(name, value){
16136         if(typeof value == "undefined" || value === null){
16137             this.clear(name);
16138             return;
16139         }
16140         this.setCookie(name, value);
16141         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16142     },
16143
16144     // private
16145     clear : function(name){
16146         this.clearCookie(name);
16147         Roo.state.CookieProvider.superclass.clear.call(this, name);
16148     },
16149
16150     // private
16151     readCookies : function(){
16152         var cookies = {};
16153         var c = document.cookie + ";";
16154         var re = /\s?(.*?)=(.*?);/g;
16155         var matches;
16156         while((matches = re.exec(c)) != null){
16157             var name = matches[1];
16158             var value = matches[2];
16159             if(name && name.substring(0,3) == "ys-"){
16160                 cookies[name.substr(3)] = this.decodeValue(value);
16161             }
16162         }
16163         return cookies;
16164     },
16165
16166     // private
16167     setCookie : function(name, value){
16168         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16169            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16170            ((this.path == null) ? "" : ("; path=" + this.path)) +
16171            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16172            ((this.secure == true) ? "; secure" : "");
16173     },
16174
16175     // private
16176     clearCookie : function(name){
16177         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16178            ((this.path == null) ? "" : ("; path=" + this.path)) +
16179            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16180            ((this.secure == true) ? "; secure" : "");
16181     }
16182 });/*
16183  * Based on:
16184  * Ext JS Library 1.1.1
16185  * Copyright(c) 2006-2007, Ext JS, LLC.
16186  *
16187  * Originally Released Under LGPL - original licence link has changed is not relivant.
16188  *
16189  * Fork - LGPL
16190  * <script type="text/javascript">
16191  */
16192  
16193
16194 /**
16195  * @class Roo.ComponentMgr
16196  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16197  * @static
16198  */
16199 Roo.ComponentMgr = function(){
16200     var all = new Roo.util.MixedCollection();
16201
16202     return {
16203         /**
16204          * Registers a component.
16205          * @param {Roo.Component} c The component
16206          */
16207         register : function(c){
16208             all.add(c);
16209         },
16210
16211         /**
16212          * Unregisters a component.
16213          * @param {Roo.Component} c The component
16214          */
16215         unregister : function(c){
16216             all.remove(c);
16217         },
16218
16219         /**
16220          * Returns a component by id
16221          * @param {String} id The component id
16222          */
16223         get : function(id){
16224             return all.get(id);
16225         },
16226
16227         /**
16228          * Registers a function that will be called when a specified component is added to ComponentMgr
16229          * @param {String} id The component id
16230          * @param {Funtction} fn The callback function
16231          * @param {Object} scope The scope of the callback
16232          */
16233         onAvailable : function(id, fn, scope){
16234             all.on("add", function(index, o){
16235                 if(o.id == id){
16236                     fn.call(scope || o, o);
16237                     all.un("add", fn, scope);
16238                 }
16239             });
16240         }
16241     };
16242 }();/*
16243  * Based on:
16244  * Ext JS Library 1.1.1
16245  * Copyright(c) 2006-2007, Ext JS, LLC.
16246  *
16247  * Originally Released Under LGPL - original licence link has changed is not relivant.
16248  *
16249  * Fork - LGPL
16250  * <script type="text/javascript">
16251  */
16252  
16253 /**
16254  * @class Roo.Component
16255  * @extends Roo.util.Observable
16256  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16257  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16258  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16259  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16260  * All visual components (widgets) that require rendering into a layout should subclass Component.
16261  * @constructor
16262  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16263  * 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
16264  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16265  */
16266 Roo.Component = function(config){
16267     config = config || {};
16268     if(config.tagName || config.dom || typeof config == "string"){ // element object
16269         config = {el: config, id: config.id || config};
16270     }
16271     this.initialConfig = config;
16272
16273     Roo.apply(this, config);
16274     this.addEvents({
16275         /**
16276          * @event disable
16277          * Fires after the component is disabled.
16278              * @param {Roo.Component} this
16279              */
16280         disable : true,
16281         /**
16282          * @event enable
16283          * Fires after the component is enabled.
16284              * @param {Roo.Component} this
16285              */
16286         enable : true,
16287         /**
16288          * @event beforeshow
16289          * Fires before the component is shown.  Return false to stop the show.
16290              * @param {Roo.Component} this
16291              */
16292         beforeshow : true,
16293         /**
16294          * @event show
16295          * Fires after the component is shown.
16296              * @param {Roo.Component} this
16297              */
16298         show : true,
16299         /**
16300          * @event beforehide
16301          * Fires before the component is hidden. Return false to stop the hide.
16302              * @param {Roo.Component} this
16303              */
16304         beforehide : true,
16305         /**
16306          * @event hide
16307          * Fires after the component is hidden.
16308              * @param {Roo.Component} this
16309              */
16310         hide : true,
16311         /**
16312          * @event beforerender
16313          * Fires before the component is rendered. Return false to stop the render.
16314              * @param {Roo.Component} this
16315              */
16316         beforerender : true,
16317         /**
16318          * @event render
16319          * Fires after the component is rendered.
16320              * @param {Roo.Component} this
16321              */
16322         render : true,
16323         /**
16324          * @event beforedestroy
16325          * Fires before the component is destroyed. Return false to stop the destroy.
16326              * @param {Roo.Component} this
16327              */
16328         beforedestroy : true,
16329         /**
16330          * @event destroy
16331          * Fires after the component is destroyed.
16332              * @param {Roo.Component} this
16333              */
16334         destroy : true
16335     });
16336     if(!this.id){
16337         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16338     }
16339     Roo.ComponentMgr.register(this);
16340     Roo.Component.superclass.constructor.call(this);
16341     this.initComponent();
16342     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16343         this.render(this.renderTo);
16344         delete this.renderTo;
16345     }
16346 };
16347
16348 /** @private */
16349 Roo.Component.AUTO_ID = 1000;
16350
16351 Roo.extend(Roo.Component, Roo.util.Observable, {
16352     /**
16353      * @scope Roo.Component.prototype
16354      * @type {Boolean}
16355      * true if this component is hidden. Read-only.
16356      */
16357     hidden : false,
16358     /**
16359      * @type {Boolean}
16360      * true if this component is disabled. Read-only.
16361      */
16362     disabled : false,
16363     /**
16364      * @type {Boolean}
16365      * true if this component has been rendered. Read-only.
16366      */
16367     rendered : false,
16368     
16369     /** @cfg {String} disableClass
16370      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16371      */
16372     disabledClass : "x-item-disabled",
16373         /** @cfg {Boolean} allowDomMove
16374          * Whether the component can move the Dom node when rendering (defaults to true).
16375          */
16376     allowDomMove : true,
16377     /** @cfg {String} hideMode (display|visibility)
16378      * How this component should hidden. Supported values are
16379      * "visibility" (css visibility), "offsets" (negative offset position) and
16380      * "display" (css display) - defaults to "display".
16381      */
16382     hideMode: 'display',
16383
16384     /** @private */
16385     ctype : "Roo.Component",
16386
16387     /**
16388      * @cfg {String} actionMode 
16389      * which property holds the element that used for  hide() / show() / disable() / enable()
16390      * default is 'el' for forms you probably want to set this to fieldEl 
16391      */
16392     actionMode : "el",
16393
16394     /** @private */
16395     getActionEl : function(){
16396         return this[this.actionMode];
16397     },
16398
16399     initComponent : Roo.emptyFn,
16400     /**
16401      * If this is a lazy rendering component, render it to its container element.
16402      * @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.
16403      */
16404     render : function(container, position){
16405         
16406         if(this.rendered){
16407             return this;
16408         }
16409         
16410         if(this.fireEvent("beforerender", this) === false){
16411             return false;
16412         }
16413         
16414         if(!container && this.el){
16415             this.el = Roo.get(this.el);
16416             container = this.el.dom.parentNode;
16417             this.allowDomMove = false;
16418         }
16419         this.container = Roo.get(container);
16420         this.rendered = true;
16421         if(position !== undefined){
16422             if(typeof position == 'number'){
16423                 position = this.container.dom.childNodes[position];
16424             }else{
16425                 position = Roo.getDom(position);
16426             }
16427         }
16428         this.onRender(this.container, position || null);
16429         if(this.cls){
16430             this.el.addClass(this.cls);
16431             delete this.cls;
16432         }
16433         if(this.style){
16434             this.el.applyStyles(this.style);
16435             delete this.style;
16436         }
16437         this.fireEvent("render", this);
16438         this.afterRender(this.container);
16439         if(this.hidden){
16440             this.hide();
16441         }
16442         if(this.disabled){
16443             this.disable();
16444         }
16445
16446         return this;
16447         
16448     },
16449
16450     /** @private */
16451     // default function is not really useful
16452     onRender : function(ct, position){
16453         if(this.el){
16454             this.el = Roo.get(this.el);
16455             if(this.allowDomMove !== false){
16456                 ct.dom.insertBefore(this.el.dom, position);
16457             }
16458         }
16459     },
16460
16461     /** @private */
16462     getAutoCreate : function(){
16463         var cfg = typeof this.autoCreate == "object" ?
16464                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16465         if(this.id && !cfg.id){
16466             cfg.id = this.id;
16467         }
16468         return cfg;
16469     },
16470
16471     /** @private */
16472     afterRender : Roo.emptyFn,
16473
16474     /**
16475      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16476      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16477      */
16478     destroy : function(){
16479         if(this.fireEvent("beforedestroy", this) !== false){
16480             this.purgeListeners();
16481             this.beforeDestroy();
16482             if(this.rendered){
16483                 this.el.removeAllListeners();
16484                 this.el.remove();
16485                 if(this.actionMode == "container"){
16486                     this.container.remove();
16487                 }
16488             }
16489             this.onDestroy();
16490             Roo.ComponentMgr.unregister(this);
16491             this.fireEvent("destroy", this);
16492         }
16493     },
16494
16495         /** @private */
16496     beforeDestroy : function(){
16497
16498     },
16499
16500         /** @private */
16501         onDestroy : function(){
16502
16503     },
16504
16505     /**
16506      * Returns the underlying {@link Roo.Element}.
16507      * @return {Roo.Element} The element
16508      */
16509     getEl : function(){
16510         return this.el;
16511     },
16512
16513     /**
16514      * Returns the id of this component.
16515      * @return {String}
16516      */
16517     getId : function(){
16518         return this.id;
16519     },
16520
16521     /**
16522      * Try to focus this component.
16523      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16524      * @return {Roo.Component} this
16525      */
16526     focus : function(selectText){
16527         if(this.rendered){
16528             this.el.focus();
16529             if(selectText === true){
16530                 this.el.dom.select();
16531             }
16532         }
16533         return this;
16534     },
16535
16536     /** @private */
16537     blur : function(){
16538         if(this.rendered){
16539             this.el.blur();
16540         }
16541         return this;
16542     },
16543
16544     /**
16545      * Disable this component.
16546      * @return {Roo.Component} this
16547      */
16548     disable : function(){
16549         if(this.rendered){
16550             this.onDisable();
16551         }
16552         this.disabled = true;
16553         this.fireEvent("disable", this);
16554         return this;
16555     },
16556
16557         // private
16558     onDisable : function(){
16559         this.getActionEl().addClass(this.disabledClass);
16560         this.el.dom.disabled = true;
16561     },
16562
16563     /**
16564      * Enable this component.
16565      * @return {Roo.Component} this
16566      */
16567     enable : function(){
16568         if(this.rendered){
16569             this.onEnable();
16570         }
16571         this.disabled = false;
16572         this.fireEvent("enable", this);
16573         return this;
16574     },
16575
16576         // private
16577     onEnable : function(){
16578         this.getActionEl().removeClass(this.disabledClass);
16579         this.el.dom.disabled = false;
16580     },
16581
16582     /**
16583      * Convenience function for setting disabled/enabled by boolean.
16584      * @param {Boolean} disabled
16585      */
16586     setDisabled : function(disabled){
16587         this[disabled ? "disable" : "enable"]();
16588     },
16589
16590     /**
16591      * Show this component.
16592      * @return {Roo.Component} this
16593      */
16594     show: function(){
16595         if(this.fireEvent("beforeshow", this) !== false){
16596             this.hidden = false;
16597             if(this.rendered){
16598                 this.onShow();
16599             }
16600             this.fireEvent("show", this);
16601         }
16602         return this;
16603     },
16604
16605     // private
16606     onShow : function(){
16607         var ae = this.getActionEl();
16608         if(this.hideMode == 'visibility'){
16609             ae.dom.style.visibility = "visible";
16610         }else if(this.hideMode == 'offsets'){
16611             ae.removeClass('x-hidden');
16612         }else{
16613             ae.dom.style.display = "";
16614         }
16615     },
16616
16617     /**
16618      * Hide this component.
16619      * @return {Roo.Component} this
16620      */
16621     hide: function(){
16622         if(this.fireEvent("beforehide", this) !== false){
16623             this.hidden = true;
16624             if(this.rendered){
16625                 this.onHide();
16626             }
16627             this.fireEvent("hide", this);
16628         }
16629         return this;
16630     },
16631
16632     // private
16633     onHide : function(){
16634         var ae = this.getActionEl();
16635         if(this.hideMode == 'visibility'){
16636             ae.dom.style.visibility = "hidden";
16637         }else if(this.hideMode == 'offsets'){
16638             ae.addClass('x-hidden');
16639         }else{
16640             ae.dom.style.display = "none";
16641         }
16642     },
16643
16644     /**
16645      * Convenience function to hide or show this component by boolean.
16646      * @param {Boolean} visible True to show, false to hide
16647      * @return {Roo.Component} this
16648      */
16649     setVisible: function(visible){
16650         if(visible) {
16651             this.show();
16652         }else{
16653             this.hide();
16654         }
16655         return this;
16656     },
16657
16658     /**
16659      * Returns true if this component is visible.
16660      */
16661     isVisible : function(){
16662         return this.getActionEl().isVisible();
16663     },
16664
16665     cloneConfig : function(overrides){
16666         overrides = overrides || {};
16667         var id = overrides.id || Roo.id();
16668         var cfg = Roo.applyIf(overrides, this.initialConfig);
16669         cfg.id = id; // prevent dup id
16670         return new this.constructor(cfg);
16671     }
16672 });/*
16673  * Based on:
16674  * Ext JS Library 1.1.1
16675  * Copyright(c) 2006-2007, Ext JS, LLC.
16676  *
16677  * Originally Released Under LGPL - original licence link has changed is not relivant.
16678  *
16679  * Fork - LGPL
16680  * <script type="text/javascript">
16681  */
16682
16683 /**
16684  * @class Roo.BoxComponent
16685  * @extends Roo.Component
16686  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16687  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16688  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16689  * layout containers.
16690  * @constructor
16691  * @param {Roo.Element/String/Object} config The configuration options.
16692  */
16693 Roo.BoxComponent = function(config){
16694     Roo.Component.call(this, config);
16695     this.addEvents({
16696         /**
16697          * @event resize
16698          * Fires after the component is resized.
16699              * @param {Roo.Component} this
16700              * @param {Number} adjWidth The box-adjusted width that was set
16701              * @param {Number} adjHeight The box-adjusted height that was set
16702              * @param {Number} rawWidth The width that was originally specified
16703              * @param {Number} rawHeight The height that was originally specified
16704              */
16705         resize : true,
16706         /**
16707          * @event move
16708          * Fires after the component is moved.
16709              * @param {Roo.Component} this
16710              * @param {Number} x The new x position
16711              * @param {Number} y The new y position
16712              */
16713         move : true
16714     });
16715 };
16716
16717 Roo.extend(Roo.BoxComponent, Roo.Component, {
16718     // private, set in afterRender to signify that the component has been rendered
16719     boxReady : false,
16720     // private, used to defer height settings to subclasses
16721     deferHeight: false,
16722     /** @cfg {Number} width
16723      * width (optional) size of component
16724      */
16725      /** @cfg {Number} height
16726      * height (optional) size of component
16727      */
16728      
16729     /**
16730      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16731      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16732      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16733      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16734      * @return {Roo.BoxComponent} this
16735      */
16736     setSize : function(w, h){
16737         // support for standard size objects
16738         if(typeof w == 'object'){
16739             h = w.height;
16740             w = w.width;
16741         }
16742         // not rendered
16743         if(!this.boxReady){
16744             this.width = w;
16745             this.height = h;
16746             return this;
16747         }
16748
16749         // prevent recalcs when not needed
16750         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16751             return this;
16752         }
16753         this.lastSize = {width: w, height: h};
16754
16755         var adj = this.adjustSize(w, h);
16756         var aw = adj.width, ah = adj.height;
16757         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16758             var rz = this.getResizeEl();
16759             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16760                 rz.setSize(aw, ah);
16761             }else if(!this.deferHeight && ah !== undefined){
16762                 rz.setHeight(ah);
16763             }else if(aw !== undefined){
16764                 rz.setWidth(aw);
16765             }
16766             this.onResize(aw, ah, w, h);
16767             this.fireEvent('resize', this, aw, ah, w, h);
16768         }
16769         return this;
16770     },
16771
16772     /**
16773      * Gets the current size of the component's underlying element.
16774      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16775      */
16776     getSize : function(){
16777         return this.el.getSize();
16778     },
16779
16780     /**
16781      * Gets the current XY position of the component's underlying element.
16782      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16783      * @return {Array} The XY position of the element (e.g., [100, 200])
16784      */
16785     getPosition : function(local){
16786         if(local === true){
16787             return [this.el.getLeft(true), this.el.getTop(true)];
16788         }
16789         return this.xy || this.el.getXY();
16790     },
16791
16792     /**
16793      * Gets the current box measurements of the component's underlying element.
16794      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16795      * @returns {Object} box An object in the format {x, y, width, height}
16796      */
16797     getBox : function(local){
16798         var s = this.el.getSize();
16799         if(local){
16800             s.x = this.el.getLeft(true);
16801             s.y = this.el.getTop(true);
16802         }else{
16803             var xy = this.xy || this.el.getXY();
16804             s.x = xy[0];
16805             s.y = xy[1];
16806         }
16807         return s;
16808     },
16809
16810     /**
16811      * Sets the current box measurements of the component's underlying element.
16812      * @param {Object} box An object in the format {x, y, width, height}
16813      * @returns {Roo.BoxComponent} this
16814      */
16815     updateBox : function(box){
16816         this.setSize(box.width, box.height);
16817         this.setPagePosition(box.x, box.y);
16818         return this;
16819     },
16820
16821     // protected
16822     getResizeEl : function(){
16823         return this.resizeEl || this.el;
16824     },
16825
16826     // protected
16827     getPositionEl : function(){
16828         return this.positionEl || this.el;
16829     },
16830
16831     /**
16832      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16833      * This method fires the move event.
16834      * @param {Number} left The new left
16835      * @param {Number} top The new top
16836      * @returns {Roo.BoxComponent} this
16837      */
16838     setPosition : function(x, y){
16839         this.x = x;
16840         this.y = y;
16841         if(!this.boxReady){
16842             return this;
16843         }
16844         var adj = this.adjustPosition(x, y);
16845         var ax = adj.x, ay = adj.y;
16846
16847         var el = this.getPositionEl();
16848         if(ax !== undefined || ay !== undefined){
16849             if(ax !== undefined && ay !== undefined){
16850                 el.setLeftTop(ax, ay);
16851             }else if(ax !== undefined){
16852                 el.setLeft(ax);
16853             }else if(ay !== undefined){
16854                 el.setTop(ay);
16855             }
16856             this.onPosition(ax, ay);
16857             this.fireEvent('move', this, ax, ay);
16858         }
16859         return this;
16860     },
16861
16862     /**
16863      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16864      * This method fires the move event.
16865      * @param {Number} x The new x position
16866      * @param {Number} y The new y position
16867      * @returns {Roo.BoxComponent} this
16868      */
16869     setPagePosition : function(x, y){
16870         this.pageX = x;
16871         this.pageY = y;
16872         if(!this.boxReady){
16873             return;
16874         }
16875         if(x === undefined || y === undefined){ // cannot translate undefined points
16876             return;
16877         }
16878         var p = this.el.translatePoints(x, y);
16879         this.setPosition(p.left, p.top);
16880         return this;
16881     },
16882
16883     // private
16884     onRender : function(ct, position){
16885         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16886         if(this.resizeEl){
16887             this.resizeEl = Roo.get(this.resizeEl);
16888         }
16889         if(this.positionEl){
16890             this.positionEl = Roo.get(this.positionEl);
16891         }
16892     },
16893
16894     // private
16895     afterRender : function(){
16896         Roo.BoxComponent.superclass.afterRender.call(this);
16897         this.boxReady = true;
16898         this.setSize(this.width, this.height);
16899         if(this.x || this.y){
16900             this.setPosition(this.x, this.y);
16901         }
16902         if(this.pageX || this.pageY){
16903             this.setPagePosition(this.pageX, this.pageY);
16904         }
16905     },
16906
16907     /**
16908      * Force the component's size to recalculate based on the underlying element's current height and width.
16909      * @returns {Roo.BoxComponent} this
16910      */
16911     syncSize : function(){
16912         delete this.lastSize;
16913         this.setSize(this.el.getWidth(), this.el.getHeight());
16914         return this;
16915     },
16916
16917     /**
16918      * Called after the component is resized, this method is empty by default but can be implemented by any
16919      * subclass that needs to perform custom logic after a resize occurs.
16920      * @param {Number} adjWidth The box-adjusted width that was set
16921      * @param {Number} adjHeight The box-adjusted height that was set
16922      * @param {Number} rawWidth The width that was originally specified
16923      * @param {Number} rawHeight The height that was originally specified
16924      */
16925     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16926
16927     },
16928
16929     /**
16930      * Called after the component is moved, this method is empty by default but can be implemented by any
16931      * subclass that needs to perform custom logic after a move occurs.
16932      * @param {Number} x The new x position
16933      * @param {Number} y The new y position
16934      */
16935     onPosition : function(x, y){
16936
16937     },
16938
16939     // private
16940     adjustSize : function(w, h){
16941         if(this.autoWidth){
16942             w = 'auto';
16943         }
16944         if(this.autoHeight){
16945             h = 'auto';
16946         }
16947         return {width : w, height: h};
16948     },
16949
16950     // private
16951     adjustPosition : function(x, y){
16952         return {x : x, y: y};
16953     }
16954 });/*
16955  * Based on:
16956  * Ext JS Library 1.1.1
16957  * Copyright(c) 2006-2007, Ext JS, LLC.
16958  *
16959  * Originally Released Under LGPL - original licence link has changed is not relivant.
16960  *
16961  * Fork - LGPL
16962  * <script type="text/javascript">
16963  */
16964  (function(){ 
16965 /**
16966  * @class Roo.Layer
16967  * @extends Roo.Element
16968  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16969  * automatic maintaining of shadow/shim positions.
16970  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16971  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16972  * you can pass a string with a CSS class name. False turns off the shadow.
16973  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16974  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16975  * @cfg {String} cls CSS class to add to the element
16976  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16977  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16978  * @constructor
16979  * @param {Object} config An object with config options.
16980  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16981  */
16982
16983 Roo.Layer = function(config, existingEl){
16984     config = config || {};
16985     var dh = Roo.DomHelper;
16986     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16987     if(existingEl){
16988         this.dom = Roo.getDom(existingEl);
16989     }
16990     if(!this.dom){
16991         var o = config.dh || {tag: "div", cls: "x-layer"};
16992         this.dom = dh.append(pel, o);
16993     }
16994     if(config.cls){
16995         this.addClass(config.cls);
16996     }
16997     this.constrain = config.constrain !== false;
16998     this.visibilityMode = Roo.Element.VISIBILITY;
16999     if(config.id){
17000         this.id = this.dom.id = config.id;
17001     }else{
17002         this.id = Roo.id(this.dom);
17003     }
17004     this.zindex = config.zindex || this.getZIndex();
17005     this.position("absolute", this.zindex);
17006     if(config.shadow){
17007         this.shadowOffset = config.shadowOffset || 4;
17008         this.shadow = new Roo.Shadow({
17009             offset : this.shadowOffset,
17010             mode : config.shadow
17011         });
17012     }else{
17013         this.shadowOffset = 0;
17014     }
17015     this.useShim = config.shim !== false && Roo.useShims;
17016     this.useDisplay = config.useDisplay;
17017     this.hide();
17018 };
17019
17020 var supr = Roo.Element.prototype;
17021
17022 // shims are shared among layer to keep from having 100 iframes
17023 var shims = [];
17024
17025 Roo.extend(Roo.Layer, Roo.Element, {
17026
17027     getZIndex : function(){
17028         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17029     },
17030
17031     getShim : function(){
17032         if(!this.useShim){
17033             return null;
17034         }
17035         if(this.shim){
17036             return this.shim;
17037         }
17038         var shim = shims.shift();
17039         if(!shim){
17040             shim = this.createShim();
17041             shim.enableDisplayMode('block');
17042             shim.dom.style.display = 'none';
17043             shim.dom.style.visibility = 'visible';
17044         }
17045         var pn = this.dom.parentNode;
17046         if(shim.dom.parentNode != pn){
17047             pn.insertBefore(shim.dom, this.dom);
17048         }
17049         shim.setStyle('z-index', this.getZIndex()-2);
17050         this.shim = shim;
17051         return shim;
17052     },
17053
17054     hideShim : function(){
17055         if(this.shim){
17056             this.shim.setDisplayed(false);
17057             shims.push(this.shim);
17058             delete this.shim;
17059         }
17060     },
17061
17062     disableShadow : function(){
17063         if(this.shadow){
17064             this.shadowDisabled = true;
17065             this.shadow.hide();
17066             this.lastShadowOffset = this.shadowOffset;
17067             this.shadowOffset = 0;
17068         }
17069     },
17070
17071     enableShadow : function(show){
17072         if(this.shadow){
17073             this.shadowDisabled = false;
17074             this.shadowOffset = this.lastShadowOffset;
17075             delete this.lastShadowOffset;
17076             if(show){
17077                 this.sync(true);
17078             }
17079         }
17080     },
17081
17082     // private
17083     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17084     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17085     sync : function(doShow){
17086         var sw = this.shadow;
17087         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17088             var sh = this.getShim();
17089
17090             var w = this.getWidth(),
17091                 h = this.getHeight();
17092
17093             var l = this.getLeft(true),
17094                 t = this.getTop(true);
17095
17096             if(sw && !this.shadowDisabled){
17097                 if(doShow && !sw.isVisible()){
17098                     sw.show(this);
17099                 }else{
17100                     sw.realign(l, t, w, h);
17101                 }
17102                 if(sh){
17103                     if(doShow){
17104                        sh.show();
17105                     }
17106                     // fit the shim behind the shadow, so it is shimmed too
17107                     var a = sw.adjusts, s = sh.dom.style;
17108                     s.left = (Math.min(l, l+a.l))+"px";
17109                     s.top = (Math.min(t, t+a.t))+"px";
17110                     s.width = (w+a.w)+"px";
17111                     s.height = (h+a.h)+"px";
17112                 }
17113             }else if(sh){
17114                 if(doShow){
17115                    sh.show();
17116                 }
17117                 sh.setSize(w, h);
17118                 sh.setLeftTop(l, t);
17119             }
17120             
17121         }
17122     },
17123
17124     // private
17125     destroy : function(){
17126         this.hideShim();
17127         if(this.shadow){
17128             this.shadow.hide();
17129         }
17130         this.removeAllListeners();
17131         var pn = this.dom.parentNode;
17132         if(pn){
17133             pn.removeChild(this.dom);
17134         }
17135         Roo.Element.uncache(this.id);
17136     },
17137
17138     remove : function(){
17139         this.destroy();
17140     },
17141
17142     // private
17143     beginUpdate : function(){
17144         this.updating = true;
17145     },
17146
17147     // private
17148     endUpdate : function(){
17149         this.updating = false;
17150         this.sync(true);
17151     },
17152
17153     // private
17154     hideUnders : function(negOffset){
17155         if(this.shadow){
17156             this.shadow.hide();
17157         }
17158         this.hideShim();
17159     },
17160
17161     // private
17162     constrainXY : function(){
17163         if(this.constrain){
17164             var vw = Roo.lib.Dom.getViewWidth(),
17165                 vh = Roo.lib.Dom.getViewHeight();
17166             var s = Roo.get(document).getScroll();
17167
17168             var xy = this.getXY();
17169             var x = xy[0], y = xy[1];   
17170             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17171             // only move it if it needs it
17172             var moved = false;
17173             // first validate right/bottom
17174             if((x + w) > vw+s.left){
17175                 x = vw - w - this.shadowOffset;
17176                 moved = true;
17177             }
17178             if((y + h) > vh+s.top){
17179                 y = vh - h - this.shadowOffset;
17180                 moved = true;
17181             }
17182             // then make sure top/left isn't negative
17183             if(x < s.left){
17184                 x = s.left;
17185                 moved = true;
17186             }
17187             if(y < s.top){
17188                 y = s.top;
17189                 moved = true;
17190             }
17191             if(moved){
17192                 if(this.avoidY){
17193                     var ay = this.avoidY;
17194                     if(y <= ay && (y+h) >= ay){
17195                         y = ay-h-5;   
17196                     }
17197                 }
17198                 xy = [x, y];
17199                 this.storeXY(xy);
17200                 supr.setXY.call(this, xy);
17201                 this.sync();
17202             }
17203         }
17204     },
17205
17206     isVisible : function(){
17207         return this.visible;    
17208     },
17209
17210     // private
17211     showAction : function(){
17212         this.visible = true; // track visibility to prevent getStyle calls
17213         if(this.useDisplay === true){
17214             this.setDisplayed("");
17215         }else if(this.lastXY){
17216             supr.setXY.call(this, this.lastXY);
17217         }else if(this.lastLT){
17218             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17219         }
17220     },
17221
17222     // private
17223     hideAction : function(){
17224         this.visible = false;
17225         if(this.useDisplay === true){
17226             this.setDisplayed(false);
17227         }else{
17228             this.setLeftTop(-10000,-10000);
17229         }
17230     },
17231
17232     // overridden Element method
17233     setVisible : function(v, a, d, c, e){
17234         if(v){
17235             this.showAction();
17236         }
17237         if(a && v){
17238             var cb = function(){
17239                 this.sync(true);
17240                 if(c){
17241                     c();
17242                 }
17243             }.createDelegate(this);
17244             supr.setVisible.call(this, true, true, d, cb, e);
17245         }else{
17246             if(!v){
17247                 this.hideUnders(true);
17248             }
17249             var cb = c;
17250             if(a){
17251                 cb = function(){
17252                     this.hideAction();
17253                     if(c){
17254                         c();
17255                     }
17256                 }.createDelegate(this);
17257             }
17258             supr.setVisible.call(this, v, a, d, cb, e);
17259             if(v){
17260                 this.sync(true);
17261             }else if(!a){
17262                 this.hideAction();
17263             }
17264         }
17265     },
17266
17267     storeXY : function(xy){
17268         delete this.lastLT;
17269         this.lastXY = xy;
17270     },
17271
17272     storeLeftTop : function(left, top){
17273         delete this.lastXY;
17274         this.lastLT = [left, top];
17275     },
17276
17277     // private
17278     beforeFx : function(){
17279         this.beforeAction();
17280         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17281     },
17282
17283     // private
17284     afterFx : function(){
17285         Roo.Layer.superclass.afterFx.apply(this, arguments);
17286         this.sync(this.isVisible());
17287     },
17288
17289     // private
17290     beforeAction : function(){
17291         if(!this.updating && this.shadow){
17292             this.shadow.hide();
17293         }
17294     },
17295
17296     // overridden Element method
17297     setLeft : function(left){
17298         this.storeLeftTop(left, this.getTop(true));
17299         supr.setLeft.apply(this, arguments);
17300         this.sync();
17301     },
17302
17303     setTop : function(top){
17304         this.storeLeftTop(this.getLeft(true), top);
17305         supr.setTop.apply(this, arguments);
17306         this.sync();
17307     },
17308
17309     setLeftTop : function(left, top){
17310         this.storeLeftTop(left, top);
17311         supr.setLeftTop.apply(this, arguments);
17312         this.sync();
17313     },
17314
17315     setXY : function(xy, a, d, c, e){
17316         this.fixDisplay();
17317         this.beforeAction();
17318         this.storeXY(xy);
17319         var cb = this.createCB(c);
17320         supr.setXY.call(this, xy, a, d, cb, e);
17321         if(!a){
17322             cb();
17323         }
17324     },
17325
17326     // private
17327     createCB : function(c){
17328         var el = this;
17329         return function(){
17330             el.constrainXY();
17331             el.sync(true);
17332             if(c){
17333                 c();
17334             }
17335         };
17336     },
17337
17338     // overridden Element method
17339     setX : function(x, a, d, c, e){
17340         this.setXY([x, this.getY()], a, d, c, e);
17341     },
17342
17343     // overridden Element method
17344     setY : function(y, a, d, c, e){
17345         this.setXY([this.getX(), y], a, d, c, e);
17346     },
17347
17348     // overridden Element method
17349     setSize : function(w, h, a, d, c, e){
17350         this.beforeAction();
17351         var cb = this.createCB(c);
17352         supr.setSize.call(this, w, h, a, d, cb, e);
17353         if(!a){
17354             cb();
17355         }
17356     },
17357
17358     // overridden Element method
17359     setWidth : function(w, a, d, c, e){
17360         this.beforeAction();
17361         var cb = this.createCB(c);
17362         supr.setWidth.call(this, w, a, d, cb, e);
17363         if(!a){
17364             cb();
17365         }
17366     },
17367
17368     // overridden Element method
17369     setHeight : function(h, a, d, c, e){
17370         this.beforeAction();
17371         var cb = this.createCB(c);
17372         supr.setHeight.call(this, h, a, d, cb, e);
17373         if(!a){
17374             cb();
17375         }
17376     },
17377
17378     // overridden Element method
17379     setBounds : function(x, y, w, h, a, d, c, e){
17380         this.beforeAction();
17381         var cb = this.createCB(c);
17382         if(!a){
17383             this.storeXY([x, y]);
17384             supr.setXY.call(this, [x, y]);
17385             supr.setSize.call(this, w, h, a, d, cb, e);
17386             cb();
17387         }else{
17388             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17389         }
17390         return this;
17391     },
17392     
17393     /**
17394      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17395      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17396      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17397      * @param {Number} zindex The new z-index to set
17398      * @return {this} The Layer
17399      */
17400     setZIndex : function(zindex){
17401         this.zindex = zindex;
17402         this.setStyle("z-index", zindex + 2);
17403         if(this.shadow){
17404             this.shadow.setZIndex(zindex + 1);
17405         }
17406         if(this.shim){
17407             this.shim.setStyle("z-index", zindex);
17408         }
17409     }
17410 });
17411 })();/*
17412  * Original code for Roojs - LGPL
17413  * <script type="text/javascript">
17414  */
17415  
17416 /**
17417  * @class Roo.XComponent
17418  * A delayed Element creator...
17419  * Or a way to group chunks of interface together.
17420  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17421  *  used in conjunction with XComponent.build() it will create an instance of each element,
17422  *  then call addxtype() to build the User interface.
17423  * 
17424  * Mypart.xyx = new Roo.XComponent({
17425
17426     parent : 'Mypart.xyz', // empty == document.element.!!
17427     order : '001',
17428     name : 'xxxx'
17429     region : 'xxxx'
17430     disabled : function() {} 
17431      
17432     tree : function() { // return an tree of xtype declared components
17433         var MODULE = this;
17434         return 
17435         {
17436             xtype : 'NestedLayoutPanel',
17437             // technicall
17438         }
17439      ]
17440  *})
17441  *
17442  *
17443  * It can be used to build a big heiracy, with parent etc.
17444  * or you can just use this to render a single compoent to a dom element
17445  * MYPART.render(Roo.Element | String(id) | dom_element )
17446  *
17447  *
17448  * Usage patterns.
17449  *
17450  * Classic Roo
17451  *
17452  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17453  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17454  *
17455  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17456  *
17457  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17458  * - if mulitple topModules exist, the last one is defined as the top module.
17459  *
17460  * Embeded Roo
17461  * 
17462  * When the top level or multiple modules are to embedded into a existing HTML page,
17463  * the parent element can container '#id' of the element where the module will be drawn.
17464  *
17465  * Bootstrap Roo
17466  *
17467  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17468  * it relies more on a include mechanism, where sub modules are included into an outer page.
17469  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17470  * 
17471  * Bootstrap Roo Included elements
17472  *
17473  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17474  * hence confusing the component builder as it thinks there are multiple top level elements. 
17475  *
17476  * String Over-ride & Translations
17477  *
17478  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17479  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17480  * are needed. @see Roo.XComponent.overlayString  
17481  * 
17482  * 
17483  * 
17484  * @extends Roo.util.Observable
17485  * @constructor
17486  * @param cfg {Object} configuration of component
17487  * 
17488  */
17489 Roo.XComponent = function(cfg) {
17490     Roo.apply(this, cfg);
17491     this.addEvents({ 
17492         /**
17493              * @event built
17494              * Fires when this the componnt is built
17495              * @param {Roo.XComponent} c the component
17496              */
17497         'built' : true
17498         
17499     });
17500     this.region = this.region || 'center'; // default..
17501     Roo.XComponent.register(this);
17502     this.modules = false;
17503     this.el = false; // where the layout goes..
17504     
17505     
17506 }
17507 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17508     /**
17509      * @property el
17510      * The created element (with Roo.factory())
17511      * @type {Roo.Layout}
17512      */
17513     el  : false,
17514     
17515     /**
17516      * @property el
17517      * for BC  - use el in new code
17518      * @type {Roo.Layout}
17519      */
17520     panel : false,
17521     
17522     /**
17523      * @property layout
17524      * for BC  - use el in new code
17525      * @type {Roo.Layout}
17526      */
17527     layout : false,
17528     
17529      /**
17530      * @cfg {Function|boolean} disabled
17531      * If this module is disabled by some rule, return true from the funtion
17532      */
17533     disabled : false,
17534     
17535     /**
17536      * @cfg {String} parent 
17537      * Name of parent element which it get xtype added to..
17538      */
17539     parent: false,
17540     
17541     /**
17542      * @cfg {String} order
17543      * Used to set the order in which elements are created (usefull for multiple tabs)
17544      */
17545     
17546     order : false,
17547     /**
17548      * @cfg {String} name
17549      * String to display while loading.
17550      */
17551     name : false,
17552     /**
17553      * @cfg {String} region
17554      * Region to render component to (defaults to center)
17555      */
17556     region : 'center',
17557     
17558     /**
17559      * @cfg {Array} items
17560      * A single item array - the first element is the root of the tree..
17561      * It's done this way to stay compatible with the Xtype system...
17562      */
17563     items : false,
17564     
17565     /**
17566      * @property _tree
17567      * The method that retuns the tree of parts that make up this compoennt 
17568      * @type {function}
17569      */
17570     _tree  : false,
17571     
17572      /**
17573      * render
17574      * render element to dom or tree
17575      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17576      */
17577     
17578     render : function(el)
17579     {
17580         
17581         el = el || false;
17582         var hp = this.parent ? 1 : 0;
17583         Roo.debug &&  Roo.log(this);
17584         
17585         var tree = this._tree ? this._tree() : this.tree();
17586
17587         
17588         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17589             // if parent is a '#.....' string, then let's use that..
17590             var ename = this.parent.substr(1);
17591             this.parent = false;
17592             Roo.debug && Roo.log(ename);
17593             switch (ename) {
17594                 case 'bootstrap-body':
17595                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17596                         // this is the BorderLayout standard?
17597                        this.parent = { el : true };
17598                        break;
17599                     }
17600                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17601                         // need to insert stuff...
17602                         this.parent =  {
17603                              el : new Roo.bootstrap.layout.Border({
17604                                  el : document.body, 
17605                      
17606                                  center: {
17607                                     titlebar: false,
17608                                     autoScroll:false,
17609                                     closeOnTab: true,
17610                                     tabPosition: 'top',
17611                                       //resizeTabs: true,
17612                                     alwaysShowTabs: true,
17613                                     hideTabs: false
17614                                      //minTabWidth: 140
17615                                  }
17616                              })
17617                         
17618                          };
17619                          break;
17620                     }
17621                          
17622                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17623                         this.parent = { el :  new  Roo.bootstrap.Body() };
17624                         Roo.debug && Roo.log("setting el to doc body");
17625                          
17626                     } else {
17627                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17628                     }
17629                     break;
17630                 case 'bootstrap':
17631                     this.parent = { el : true};
17632                     // fall through
17633                 default:
17634                     el = Roo.get(ename);
17635                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17636                         this.parent = { el : true};
17637                     }
17638                     
17639                     break;
17640             }
17641                 
17642             
17643             if (!el && !this.parent) {
17644                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17645                 return;
17646             }
17647         }
17648         
17649         Roo.debug && Roo.log("EL:");
17650         Roo.debug && Roo.log(el);
17651         Roo.debug && Roo.log("this.parent.el:");
17652         Roo.debug && Roo.log(this.parent.el);
17653         
17654
17655         // altertive root elements ??? - we need a better way to indicate these.
17656         var is_alt = Roo.XComponent.is_alt ||
17657                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17658                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17659                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17660         
17661         
17662         
17663         if (!this.parent && is_alt) {
17664             //el = Roo.get(document.body);
17665             this.parent = { el : true };
17666         }
17667             
17668             
17669         
17670         if (!this.parent) {
17671             
17672             Roo.debug && Roo.log("no parent - creating one");
17673             
17674             el = el ? Roo.get(el) : false;      
17675             
17676             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17677                 
17678                 this.parent =  {
17679                     el : new Roo.bootstrap.layout.Border({
17680                         el: el || document.body,
17681                     
17682                         center: {
17683                             titlebar: false,
17684                             autoScroll:false,
17685                             closeOnTab: true,
17686                             tabPosition: 'top',
17687                              //resizeTabs: true,
17688                             alwaysShowTabs: false,
17689                             hideTabs: true,
17690                             minTabWidth: 140,
17691                             overflow: 'visible'
17692                          }
17693                      })
17694                 };
17695             } else {
17696             
17697                 // it's a top level one..
17698                 this.parent =  {
17699                     el : new Roo.BorderLayout(el || document.body, {
17700                         center: {
17701                             titlebar: false,
17702                             autoScroll:false,
17703                             closeOnTab: true,
17704                             tabPosition: 'top',
17705                              //resizeTabs: true,
17706                             alwaysShowTabs: el && hp? false :  true,
17707                             hideTabs: el || !hp ? true :  false,
17708                             minTabWidth: 140
17709                          }
17710                     })
17711                 };
17712             }
17713         }
17714         
17715         if (!this.parent.el) {
17716                 // probably an old style ctor, which has been disabled.
17717                 return;
17718
17719         }
17720                 // The 'tree' method is  '_tree now' 
17721             
17722         tree.region = tree.region || this.region;
17723         var is_body = false;
17724         if (this.parent.el === true) {
17725             // bootstrap... - body..
17726             if (el) {
17727                 tree.el = el;
17728             }
17729             this.parent.el = Roo.factory(tree);
17730             is_body = true;
17731         }
17732         
17733         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17734         this.fireEvent('built', this);
17735         
17736         this.panel = this.el;
17737         this.layout = this.panel.layout;
17738         this.parentLayout = this.parent.layout  || false;  
17739          
17740     }
17741     
17742 });
17743
17744 Roo.apply(Roo.XComponent, {
17745     /**
17746      * @property  hideProgress
17747      * true to disable the building progress bar.. usefull on single page renders.
17748      * @type Boolean
17749      */
17750     hideProgress : false,
17751     /**
17752      * @property  buildCompleted
17753      * True when the builder has completed building the interface.
17754      * @type Boolean
17755      */
17756     buildCompleted : false,
17757      
17758     /**
17759      * @property  topModule
17760      * the upper most module - uses document.element as it's constructor.
17761      * @type Object
17762      */
17763      
17764     topModule  : false,
17765       
17766     /**
17767      * @property  modules
17768      * array of modules to be created by registration system.
17769      * @type {Array} of Roo.XComponent
17770      */
17771     
17772     modules : [],
17773     /**
17774      * @property  elmodules
17775      * array of modules to be created by which use #ID 
17776      * @type {Array} of Roo.XComponent
17777      */
17778      
17779     elmodules : [],
17780
17781      /**
17782      * @property  is_alt
17783      * Is an alternative Root - normally used by bootstrap or other systems,
17784      *    where the top element in the tree can wrap 'body' 
17785      * @type {boolean}  (default false)
17786      */
17787      
17788     is_alt : false,
17789     /**
17790      * @property  build_from_html
17791      * Build elements from html - used by bootstrap HTML stuff 
17792      *    - this is cleared after build is completed
17793      * @type {boolean}    (default false)
17794      */
17795      
17796     build_from_html : false,
17797     /**
17798      * Register components to be built later.
17799      *
17800      * This solves the following issues
17801      * - Building is not done on page load, but after an authentication process has occured.
17802      * - Interface elements are registered on page load
17803      * - Parent Interface elements may not be loaded before child, so this handles that..
17804      * 
17805      *
17806      * example:
17807      * 
17808      * MyApp.register({
17809           order : '000001',
17810           module : 'Pman.Tab.projectMgr',
17811           region : 'center',
17812           parent : 'Pman.layout',
17813           disabled : false,  // or use a function..
17814         })
17815      
17816      * * @param {Object} details about module
17817      */
17818     register : function(obj) {
17819                 
17820         Roo.XComponent.event.fireEvent('register', obj);
17821         switch(typeof(obj.disabled) ) {
17822                 
17823             case 'undefined':
17824                 break;
17825             
17826             case 'function':
17827                 if ( obj.disabled() ) {
17828                         return;
17829                 }
17830                 break;
17831             
17832             default:
17833                 if (obj.disabled || obj.region == '#disabled') {
17834                         return;
17835                 }
17836                 break;
17837         }
17838                 
17839         this.modules.push(obj);
17840          
17841     },
17842     /**
17843      * convert a string to an object..
17844      * eg. 'AAA.BBB' -> finds AAA.BBB
17845
17846      */
17847     
17848     toObject : function(str)
17849     {
17850         if (!str || typeof(str) == 'object') {
17851             return str;
17852         }
17853         if (str.substring(0,1) == '#') {
17854             return str;
17855         }
17856
17857         var ar = str.split('.');
17858         var rt, o;
17859         rt = ar.shift();
17860             /** eval:var:o */
17861         try {
17862             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17863         } catch (e) {
17864             throw "Module not found : " + str;
17865         }
17866         
17867         if (o === false) {
17868             throw "Module not found : " + str;
17869         }
17870         Roo.each(ar, function(e) {
17871             if (typeof(o[e]) == 'undefined') {
17872                 throw "Module not found : " + str;
17873             }
17874             o = o[e];
17875         });
17876         
17877         return o;
17878         
17879     },
17880     
17881     
17882     /**
17883      * move modules into their correct place in the tree..
17884      * 
17885      */
17886     preBuild : function ()
17887     {
17888         var _t = this;
17889         Roo.each(this.modules , function (obj)
17890         {
17891             Roo.XComponent.event.fireEvent('beforebuild', obj);
17892             
17893             var opar = obj.parent;
17894             try { 
17895                 obj.parent = this.toObject(opar);
17896             } catch(e) {
17897                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17898                 return;
17899             }
17900             
17901             if (!obj.parent) {
17902                 Roo.debug && Roo.log("GOT top level module");
17903                 Roo.debug && Roo.log(obj);
17904                 obj.modules = new Roo.util.MixedCollection(false, 
17905                     function(o) { return o.order + '' }
17906                 );
17907                 this.topModule = obj;
17908                 return;
17909             }
17910                         // parent is a string (usually a dom element name..)
17911             if (typeof(obj.parent) == 'string') {
17912                 this.elmodules.push(obj);
17913                 return;
17914             }
17915             if (obj.parent.constructor != Roo.XComponent) {
17916                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17917             }
17918             if (!obj.parent.modules) {
17919                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17920                     function(o) { return o.order + '' }
17921                 );
17922             }
17923             if (obj.parent.disabled) {
17924                 obj.disabled = true;
17925             }
17926             obj.parent.modules.add(obj);
17927         }, this);
17928     },
17929     
17930      /**
17931      * make a list of modules to build.
17932      * @return {Array} list of modules. 
17933      */ 
17934     
17935     buildOrder : function()
17936     {
17937         var _this = this;
17938         var cmp = function(a,b) {   
17939             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17940         };
17941         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17942             throw "No top level modules to build";
17943         }
17944         
17945         // make a flat list in order of modules to build.
17946         var mods = this.topModule ? [ this.topModule ] : [];
17947                 
17948         
17949         // elmodules (is a list of DOM based modules )
17950         Roo.each(this.elmodules, function(e) {
17951             mods.push(e);
17952             if (!this.topModule &&
17953                 typeof(e.parent) == 'string' &&
17954                 e.parent.substring(0,1) == '#' &&
17955                 Roo.get(e.parent.substr(1))
17956                ) {
17957                 
17958                 _this.topModule = e;
17959             }
17960             
17961         });
17962
17963         
17964         // add modules to their parents..
17965         var addMod = function(m) {
17966             Roo.debug && Roo.log("build Order: add: " + m.name);
17967                 
17968             mods.push(m);
17969             if (m.modules && !m.disabled) {
17970                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17971                 m.modules.keySort('ASC',  cmp );
17972                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17973     
17974                 m.modules.each(addMod);
17975             } else {
17976                 Roo.debug && Roo.log("build Order: no child modules");
17977             }
17978             // not sure if this is used any more..
17979             if (m.finalize) {
17980                 m.finalize.name = m.name + " (clean up) ";
17981                 mods.push(m.finalize);
17982             }
17983             
17984         }
17985         if (this.topModule && this.topModule.modules) { 
17986             this.topModule.modules.keySort('ASC',  cmp );
17987             this.topModule.modules.each(addMod);
17988         } 
17989         return mods;
17990     },
17991     
17992      /**
17993      * Build the registered modules.
17994      * @param {Object} parent element.
17995      * @param {Function} optional method to call after module has been added.
17996      * 
17997      */ 
17998    
17999     build : function(opts) 
18000     {
18001         
18002         if (typeof(opts) != 'undefined') {
18003             Roo.apply(this,opts);
18004         }
18005         
18006         this.preBuild();
18007         var mods = this.buildOrder();
18008       
18009         //this.allmods = mods;
18010         //Roo.debug && Roo.log(mods);
18011         //return;
18012         if (!mods.length) { // should not happen
18013             throw "NO modules!!!";
18014         }
18015         
18016         
18017         var msg = "Building Interface...";
18018         // flash it up as modal - so we store the mask!?
18019         if (!this.hideProgress && Roo.MessageBox) {
18020             Roo.MessageBox.show({ title: 'loading' });
18021             Roo.MessageBox.show({
18022                title: "Please wait...",
18023                msg: msg,
18024                width:450,
18025                progress:true,
18026                buttons : false,
18027                closable:false,
18028                modal: false
18029               
18030             });
18031         }
18032         var total = mods.length;
18033         
18034         var _this = this;
18035         var progressRun = function() {
18036             if (!mods.length) {
18037                 Roo.debug && Roo.log('hide?');
18038                 if (!this.hideProgress && Roo.MessageBox) {
18039                     Roo.MessageBox.hide();
18040                 }
18041                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18042                 
18043                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18044                 
18045                 // THE END...
18046                 return false;   
18047             }
18048             
18049             var m = mods.shift();
18050             
18051             
18052             Roo.debug && Roo.log(m);
18053             // not sure if this is supported any more.. - modules that are are just function
18054             if (typeof(m) == 'function') { 
18055                 m.call(this);
18056                 return progressRun.defer(10, _this);
18057             } 
18058             
18059             
18060             msg = "Building Interface " + (total  - mods.length) + 
18061                     " of " + total + 
18062                     (m.name ? (' - ' + m.name) : '');
18063                         Roo.debug && Roo.log(msg);
18064             if (!_this.hideProgress &&  Roo.MessageBox) { 
18065                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18066             }
18067             
18068          
18069             // is the module disabled?
18070             var disabled = (typeof(m.disabled) == 'function') ?
18071                 m.disabled.call(m.module.disabled) : m.disabled;    
18072             
18073             
18074             if (disabled) {
18075                 return progressRun(); // we do not update the display!
18076             }
18077             
18078             // now build 
18079             
18080                         
18081                         
18082             m.render();
18083             // it's 10 on top level, and 1 on others??? why...
18084             return progressRun.defer(10, _this);
18085              
18086         }
18087         progressRun.defer(1, _this);
18088      
18089         
18090         
18091     },
18092     /**
18093      * Overlay a set of modified strings onto a component
18094      * This is dependant on our builder exporting the strings and 'named strings' elements.
18095      * 
18096      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18097      * @param {Object} associative array of 'named' string and it's new value.
18098      * 
18099      */
18100         overlayStrings : function( component, strings )
18101     {
18102         if (typeof(component['_named_strings']) == 'undefined') {
18103             throw "ERROR: component does not have _named_strings";
18104         }
18105         for ( var k in strings ) {
18106             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18107             if (md !== false) {
18108                 component['_strings'][md] = strings[k];
18109             } else {
18110                 Roo.log('could not find named string: ' + k + ' in');
18111                 Roo.log(component);
18112             }
18113             
18114         }
18115         
18116     },
18117     
18118         
18119         /**
18120          * Event Object.
18121          *
18122          *
18123          */
18124         event: false, 
18125     /**
18126          * wrapper for event.on - aliased later..  
18127          * Typically use to register a event handler for register:
18128          *
18129          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18130          *
18131          */
18132     on : false
18133    
18134     
18135     
18136 });
18137
18138 Roo.XComponent.event = new Roo.util.Observable({
18139                 events : { 
18140                         /**
18141                          * @event register
18142                          * Fires when an Component is registered,
18143                          * set the disable property on the Component to stop registration.
18144                          * @param {Roo.XComponent} c the component being registerd.
18145                          * 
18146                          */
18147                         'register' : true,
18148             /**
18149                          * @event beforebuild
18150                          * Fires before each Component is built
18151                          * can be used to apply permissions.
18152                          * @param {Roo.XComponent} c the component being registerd.
18153                          * 
18154                          */
18155                         'beforebuild' : true,
18156                         /**
18157                          * @event buildcomplete
18158                          * Fires on the top level element when all elements have been built
18159                          * @param {Roo.XComponent} the top level component.
18160                          */
18161                         'buildcomplete' : true
18162                         
18163                 }
18164 });
18165
18166 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18167  //
18168  /**
18169  * marked - a markdown parser
18170  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18171  * https://github.com/chjj/marked
18172  */
18173
18174
18175 /**
18176  *
18177  * Roo.Markdown - is a very crude wrapper around marked..
18178  *
18179  * usage:
18180  * 
18181  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18182  * 
18183  * Note: move the sample code to the bottom of this
18184  * file before uncommenting it.
18185  *
18186  */
18187
18188 Roo.Markdown = {};
18189 Roo.Markdown.toHtml = function(text) {
18190     
18191     var c = new Roo.Markdown.marked.setOptions({
18192             renderer: new Roo.Markdown.marked.Renderer(),
18193             gfm: true,
18194             tables: true,
18195             breaks: false,
18196             pedantic: false,
18197             sanitize: false,
18198             smartLists: true,
18199             smartypants: false
18200           });
18201     // A FEW HACKS!!?
18202     
18203     text = text.replace(/\\\n/g,' ');
18204     return Roo.Markdown.marked(text);
18205 };
18206 //
18207 // converter
18208 //
18209 // Wraps all "globals" so that the only thing
18210 // exposed is makeHtml().
18211 //
18212 (function() {
18213     
18214      /**
18215          * eval:var:escape
18216          * eval:var:unescape
18217          * eval:var:replace
18218          */
18219       
18220     /**
18221      * Helpers
18222      */
18223     
18224     var escape = function (html, encode) {
18225       return html
18226         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18227         .replace(/</g, '&lt;')
18228         .replace(/>/g, '&gt;')
18229         .replace(/"/g, '&quot;')
18230         .replace(/'/g, '&#39;');
18231     }
18232     
18233     var unescape = function (html) {
18234         // explicitly match decimal, hex, and named HTML entities 
18235       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18236         n = n.toLowerCase();
18237         if (n === 'colon') { return ':'; }
18238         if (n.charAt(0) === '#') {
18239           return n.charAt(1) === 'x'
18240             ? String.fromCharCode(parseInt(n.substring(2), 16))
18241             : String.fromCharCode(+n.substring(1));
18242         }
18243         return '';
18244       });
18245     }
18246     
18247     var replace = function (regex, opt) {
18248       regex = regex.source;
18249       opt = opt || '';
18250       return function self(name, val) {
18251         if (!name) { return new RegExp(regex, opt); }
18252         val = val.source || val;
18253         val = val.replace(/(^|[^\[])\^/g, '$1');
18254         regex = regex.replace(name, val);
18255         return self;
18256       };
18257     }
18258
18259
18260          /**
18261          * eval:var:noop
18262     */
18263     var noop = function () {}
18264     noop.exec = noop;
18265     
18266          /**
18267          * eval:var:merge
18268     */
18269     var merge = function (obj) {
18270       var i = 1
18271         , target
18272         , key;
18273     
18274       for (; i < arguments.length; i++) {
18275         target = arguments[i];
18276         for (key in target) {
18277           if (Object.prototype.hasOwnProperty.call(target, key)) {
18278             obj[key] = target[key];
18279           }
18280         }
18281       }
18282     
18283       return obj;
18284     }
18285     
18286     
18287     /**
18288      * Block-Level Grammar
18289      */
18290     
18291     
18292     
18293     
18294     var block = {
18295       newline: /^\n+/,
18296       code: /^( {4}[^\n]+\n*)+/,
18297       fences: noop,
18298       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18299       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18300       nptable: noop,
18301       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18302       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18303       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18304       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18305       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18306       table: noop,
18307       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18308       text: /^[^\n]+/
18309     };
18310     
18311     block.bullet = /(?:[*+-]|\d+\.)/;
18312     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18313     block.item = replace(block.item, 'gm')
18314       (/bull/g, block.bullet)
18315       ();
18316     
18317     block.list = replace(block.list)
18318       (/bull/g, block.bullet)
18319       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18320       ('def', '\\n+(?=' + block.def.source + ')')
18321       ();
18322     
18323     block.blockquote = replace(block.blockquote)
18324       ('def', block.def)
18325       ();
18326     
18327     block._tag = '(?!(?:'
18328       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18329       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18330       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18331     
18332     block.html = replace(block.html)
18333       ('comment', /<!--[\s\S]*?-->/)
18334       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18335       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18336       (/tag/g, block._tag)
18337       ();
18338     
18339     block.paragraph = replace(block.paragraph)
18340       ('hr', block.hr)
18341       ('heading', block.heading)
18342       ('lheading', block.lheading)
18343       ('blockquote', block.blockquote)
18344       ('tag', '<' + block._tag)
18345       ('def', block.def)
18346       ();
18347     
18348     /**
18349      * Normal Block Grammar
18350      */
18351     
18352     block.normal = merge({}, block);
18353     
18354     /**
18355      * GFM Block Grammar
18356      */
18357     
18358     block.gfm = merge({}, block.normal, {
18359       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18360       paragraph: /^/,
18361       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18362     });
18363     
18364     block.gfm.paragraph = replace(block.paragraph)
18365       ('(?!', '(?!'
18366         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18367         + block.list.source.replace('\\1', '\\3') + '|')
18368       ();
18369     
18370     /**
18371      * GFM + Tables Block Grammar
18372      */
18373     
18374     block.tables = merge({}, block.gfm, {
18375       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18376       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18377     });
18378     
18379     /**
18380      * Block Lexer
18381      */
18382     
18383     var Lexer = function (options) {
18384       this.tokens = [];
18385       this.tokens.links = {};
18386       this.options = options || marked.defaults;
18387       this.rules = block.normal;
18388     
18389       if (this.options.gfm) {
18390         if (this.options.tables) {
18391           this.rules = block.tables;
18392         } else {
18393           this.rules = block.gfm;
18394         }
18395       }
18396     }
18397     
18398     /**
18399      * Expose Block Rules
18400      */
18401     
18402     Lexer.rules = block;
18403     
18404     /**
18405      * Static Lex Method
18406      */
18407     
18408     Lexer.lex = function(src, options) {
18409       var lexer = new Lexer(options);
18410       return lexer.lex(src);
18411     };
18412     
18413     /**
18414      * Preprocessing
18415      */
18416     
18417     Lexer.prototype.lex = function(src) {
18418       src = src
18419         .replace(/\r\n|\r/g, '\n')
18420         .replace(/\t/g, '    ')
18421         .replace(/\u00a0/g, ' ')
18422         .replace(/\u2424/g, '\n');
18423     
18424       return this.token(src, true);
18425     };
18426     
18427     /**
18428      * Lexing
18429      */
18430     
18431     Lexer.prototype.token = function(src, top, bq) {
18432       var src = src.replace(/^ +$/gm, '')
18433         , next
18434         , loose
18435         , cap
18436         , bull
18437         , b
18438         , item
18439         , space
18440         , i
18441         , l;
18442     
18443       while (src) {
18444         // newline
18445         if (cap = this.rules.newline.exec(src)) {
18446           src = src.substring(cap[0].length);
18447           if (cap[0].length > 1) {
18448             this.tokens.push({
18449               type: 'space'
18450             });
18451           }
18452         }
18453     
18454         // code
18455         if (cap = this.rules.code.exec(src)) {
18456           src = src.substring(cap[0].length);
18457           cap = cap[0].replace(/^ {4}/gm, '');
18458           this.tokens.push({
18459             type: 'code',
18460             text: !this.options.pedantic
18461               ? cap.replace(/\n+$/, '')
18462               : cap
18463           });
18464           continue;
18465         }
18466     
18467         // fences (gfm)
18468         if (cap = this.rules.fences.exec(src)) {
18469           src = src.substring(cap[0].length);
18470           this.tokens.push({
18471             type: 'code',
18472             lang: cap[2],
18473             text: cap[3] || ''
18474           });
18475           continue;
18476         }
18477     
18478         // heading
18479         if (cap = this.rules.heading.exec(src)) {
18480           src = src.substring(cap[0].length);
18481           this.tokens.push({
18482             type: 'heading',
18483             depth: cap[1].length,
18484             text: cap[2]
18485           });
18486           continue;
18487         }
18488     
18489         // table no leading pipe (gfm)
18490         if (top && (cap = this.rules.nptable.exec(src))) {
18491           src = src.substring(cap[0].length);
18492     
18493           item = {
18494             type: 'table',
18495             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18496             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18497             cells: cap[3].replace(/\n$/, '').split('\n')
18498           };
18499     
18500           for (i = 0; i < item.align.length; i++) {
18501             if (/^ *-+: *$/.test(item.align[i])) {
18502               item.align[i] = 'right';
18503             } else if (/^ *:-+: *$/.test(item.align[i])) {
18504               item.align[i] = 'center';
18505             } else if (/^ *:-+ *$/.test(item.align[i])) {
18506               item.align[i] = 'left';
18507             } else {
18508               item.align[i] = null;
18509             }
18510           }
18511     
18512           for (i = 0; i < item.cells.length; i++) {
18513             item.cells[i] = item.cells[i].split(/ *\| */);
18514           }
18515     
18516           this.tokens.push(item);
18517     
18518           continue;
18519         }
18520     
18521         // lheading
18522         if (cap = this.rules.lheading.exec(src)) {
18523           src = src.substring(cap[0].length);
18524           this.tokens.push({
18525             type: 'heading',
18526             depth: cap[2] === '=' ? 1 : 2,
18527             text: cap[1]
18528           });
18529           continue;
18530         }
18531     
18532         // hr
18533         if (cap = this.rules.hr.exec(src)) {
18534           src = src.substring(cap[0].length);
18535           this.tokens.push({
18536             type: 'hr'
18537           });
18538           continue;
18539         }
18540     
18541         // blockquote
18542         if (cap = this.rules.blockquote.exec(src)) {
18543           src = src.substring(cap[0].length);
18544     
18545           this.tokens.push({
18546             type: 'blockquote_start'
18547           });
18548     
18549           cap = cap[0].replace(/^ *> ?/gm, '');
18550     
18551           // Pass `top` to keep the current
18552           // "toplevel" state. This is exactly
18553           // how markdown.pl works.
18554           this.token(cap, top, true);
18555     
18556           this.tokens.push({
18557             type: 'blockquote_end'
18558           });
18559     
18560           continue;
18561         }
18562     
18563         // list
18564         if (cap = this.rules.list.exec(src)) {
18565           src = src.substring(cap[0].length);
18566           bull = cap[2];
18567     
18568           this.tokens.push({
18569             type: 'list_start',
18570             ordered: bull.length > 1
18571           });
18572     
18573           // Get each top-level item.
18574           cap = cap[0].match(this.rules.item);
18575     
18576           next = false;
18577           l = cap.length;
18578           i = 0;
18579     
18580           for (; i < l; i++) {
18581             item = cap[i];
18582     
18583             // Remove the list item's bullet
18584             // so it is seen as the next token.
18585             space = item.length;
18586             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18587     
18588             // Outdent whatever the
18589             // list item contains. Hacky.
18590             if (~item.indexOf('\n ')) {
18591               space -= item.length;
18592               item = !this.options.pedantic
18593                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18594                 : item.replace(/^ {1,4}/gm, '');
18595             }
18596     
18597             // Determine whether the next list item belongs here.
18598             // Backpedal if it does not belong in this list.
18599             if (this.options.smartLists && i !== l - 1) {
18600               b = block.bullet.exec(cap[i + 1])[0];
18601               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18602                 src = cap.slice(i + 1).join('\n') + src;
18603                 i = l - 1;
18604               }
18605             }
18606     
18607             // Determine whether item is loose or not.
18608             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18609             // for discount behavior.
18610             loose = next || /\n\n(?!\s*$)/.test(item);
18611             if (i !== l - 1) {
18612               next = item.charAt(item.length - 1) === '\n';
18613               if (!loose) { loose = next; }
18614             }
18615     
18616             this.tokens.push({
18617               type: loose
18618                 ? 'loose_item_start'
18619                 : 'list_item_start'
18620             });
18621     
18622             // Recurse.
18623             this.token(item, false, bq);
18624     
18625             this.tokens.push({
18626               type: 'list_item_end'
18627             });
18628           }
18629     
18630           this.tokens.push({
18631             type: 'list_end'
18632           });
18633     
18634           continue;
18635         }
18636     
18637         // html
18638         if (cap = this.rules.html.exec(src)) {
18639           src = src.substring(cap[0].length);
18640           this.tokens.push({
18641             type: this.options.sanitize
18642               ? 'paragraph'
18643               : 'html',
18644             pre: !this.options.sanitizer
18645               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18646             text: cap[0]
18647           });
18648           continue;
18649         }
18650     
18651         // def
18652         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18653           src = src.substring(cap[0].length);
18654           this.tokens.links[cap[1].toLowerCase()] = {
18655             href: cap[2],
18656             title: cap[3]
18657           };
18658           continue;
18659         }
18660     
18661         // table (gfm)
18662         if (top && (cap = this.rules.table.exec(src))) {
18663           src = src.substring(cap[0].length);
18664     
18665           item = {
18666             type: 'table',
18667             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18668             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18669             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18670           };
18671     
18672           for (i = 0; i < item.align.length; i++) {
18673             if (/^ *-+: *$/.test(item.align[i])) {
18674               item.align[i] = 'right';
18675             } else if (/^ *:-+: *$/.test(item.align[i])) {
18676               item.align[i] = 'center';
18677             } else if (/^ *:-+ *$/.test(item.align[i])) {
18678               item.align[i] = 'left';
18679             } else {
18680               item.align[i] = null;
18681             }
18682           }
18683     
18684           for (i = 0; i < item.cells.length; i++) {
18685             item.cells[i] = item.cells[i]
18686               .replace(/^ *\| *| *\| *$/g, '')
18687               .split(/ *\| */);
18688           }
18689     
18690           this.tokens.push(item);
18691     
18692           continue;
18693         }
18694     
18695         // top-level paragraph
18696         if (top && (cap = this.rules.paragraph.exec(src))) {
18697           src = src.substring(cap[0].length);
18698           this.tokens.push({
18699             type: 'paragraph',
18700             text: cap[1].charAt(cap[1].length - 1) === '\n'
18701               ? cap[1].slice(0, -1)
18702               : cap[1]
18703           });
18704           continue;
18705         }
18706     
18707         // text
18708         if (cap = this.rules.text.exec(src)) {
18709           // Top-level should never reach here.
18710           src = src.substring(cap[0].length);
18711           this.tokens.push({
18712             type: 'text',
18713             text: cap[0]
18714           });
18715           continue;
18716         }
18717     
18718         if (src) {
18719           throw new
18720             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18721         }
18722       }
18723     
18724       return this.tokens;
18725     };
18726     
18727     /**
18728      * Inline-Level Grammar
18729      */
18730     
18731     var inline = {
18732       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18733       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18734       url: noop,
18735       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18736       link: /^!?\[(inside)\]\(href\)/,
18737       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18738       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18739       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18740       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18741       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18742       br: /^ {2,}\n(?!\s*$)/,
18743       del: noop,
18744       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18745     };
18746     
18747     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18748     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18749     
18750     inline.link = replace(inline.link)
18751       ('inside', inline._inside)
18752       ('href', inline._href)
18753       ();
18754     
18755     inline.reflink = replace(inline.reflink)
18756       ('inside', inline._inside)
18757       ();
18758     
18759     /**
18760      * Normal Inline Grammar
18761      */
18762     
18763     inline.normal = merge({}, inline);
18764     
18765     /**
18766      * Pedantic Inline Grammar
18767      */
18768     
18769     inline.pedantic = merge({}, inline.normal, {
18770       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18771       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18772     });
18773     
18774     /**
18775      * GFM Inline Grammar
18776      */
18777     
18778     inline.gfm = merge({}, inline.normal, {
18779       escape: replace(inline.escape)('])', '~|])')(),
18780       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18781       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18782       text: replace(inline.text)
18783         (']|', '~]|')
18784         ('|', '|https?://|')
18785         ()
18786     });
18787     
18788     /**
18789      * GFM + Line Breaks Inline Grammar
18790      */
18791     
18792     inline.breaks = merge({}, inline.gfm, {
18793       br: replace(inline.br)('{2,}', '*')(),
18794       text: replace(inline.gfm.text)('{2,}', '*')()
18795     });
18796     
18797     /**
18798      * Inline Lexer & Compiler
18799      */
18800     
18801     var InlineLexer  = function (links, options) {
18802       this.options = options || marked.defaults;
18803       this.links = links;
18804       this.rules = inline.normal;
18805       this.renderer = this.options.renderer || new Renderer;
18806       this.renderer.options = this.options;
18807     
18808       if (!this.links) {
18809         throw new
18810           Error('Tokens array requires a `links` property.');
18811       }
18812     
18813       if (this.options.gfm) {
18814         if (this.options.breaks) {
18815           this.rules = inline.breaks;
18816         } else {
18817           this.rules = inline.gfm;
18818         }
18819       } else if (this.options.pedantic) {
18820         this.rules = inline.pedantic;
18821       }
18822     }
18823     
18824     /**
18825      * Expose Inline Rules
18826      */
18827     
18828     InlineLexer.rules = inline;
18829     
18830     /**
18831      * Static Lexing/Compiling Method
18832      */
18833     
18834     InlineLexer.output = function(src, links, options) {
18835       var inline = new InlineLexer(links, options);
18836       return inline.output(src);
18837     };
18838     
18839     /**
18840      * Lexing/Compiling
18841      */
18842     
18843     InlineLexer.prototype.output = function(src) {
18844       var out = ''
18845         , link
18846         , text
18847         , href
18848         , cap;
18849     
18850       while (src) {
18851         // escape
18852         if (cap = this.rules.escape.exec(src)) {
18853           src = src.substring(cap[0].length);
18854           out += cap[1];
18855           continue;
18856         }
18857     
18858         // autolink
18859         if (cap = this.rules.autolink.exec(src)) {
18860           src = src.substring(cap[0].length);
18861           if (cap[2] === '@') {
18862             text = cap[1].charAt(6) === ':'
18863               ? this.mangle(cap[1].substring(7))
18864               : this.mangle(cap[1]);
18865             href = this.mangle('mailto:') + text;
18866           } else {
18867             text = escape(cap[1]);
18868             href = text;
18869           }
18870           out += this.renderer.link(href, null, text);
18871           continue;
18872         }
18873     
18874         // url (gfm)
18875         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18876           src = src.substring(cap[0].length);
18877           text = escape(cap[1]);
18878           href = text;
18879           out += this.renderer.link(href, null, text);
18880           continue;
18881         }
18882     
18883         // tag
18884         if (cap = this.rules.tag.exec(src)) {
18885           if (!this.inLink && /^<a /i.test(cap[0])) {
18886             this.inLink = true;
18887           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18888             this.inLink = false;
18889           }
18890           src = src.substring(cap[0].length);
18891           out += this.options.sanitize
18892             ? this.options.sanitizer
18893               ? this.options.sanitizer(cap[0])
18894               : escape(cap[0])
18895             : cap[0];
18896           continue;
18897         }
18898     
18899         // link
18900         if (cap = this.rules.link.exec(src)) {
18901           src = src.substring(cap[0].length);
18902           this.inLink = true;
18903           out += this.outputLink(cap, {
18904             href: cap[2],
18905             title: cap[3]
18906           });
18907           this.inLink = false;
18908           continue;
18909         }
18910     
18911         // reflink, nolink
18912         if ((cap = this.rules.reflink.exec(src))
18913             || (cap = this.rules.nolink.exec(src))) {
18914           src = src.substring(cap[0].length);
18915           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18916           link = this.links[link.toLowerCase()];
18917           if (!link || !link.href) {
18918             out += cap[0].charAt(0);
18919             src = cap[0].substring(1) + src;
18920             continue;
18921           }
18922           this.inLink = true;
18923           out += this.outputLink(cap, link);
18924           this.inLink = false;
18925           continue;
18926         }
18927     
18928         // strong
18929         if (cap = this.rules.strong.exec(src)) {
18930           src = src.substring(cap[0].length);
18931           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18932           continue;
18933         }
18934     
18935         // em
18936         if (cap = this.rules.em.exec(src)) {
18937           src = src.substring(cap[0].length);
18938           out += this.renderer.em(this.output(cap[2] || cap[1]));
18939           continue;
18940         }
18941     
18942         // code
18943         if (cap = this.rules.code.exec(src)) {
18944           src = src.substring(cap[0].length);
18945           out += this.renderer.codespan(escape(cap[2], true));
18946           continue;
18947         }
18948     
18949         // br
18950         if (cap = this.rules.br.exec(src)) {
18951           src = src.substring(cap[0].length);
18952           out += this.renderer.br();
18953           continue;
18954         }
18955     
18956         // del (gfm)
18957         if (cap = this.rules.del.exec(src)) {
18958           src = src.substring(cap[0].length);
18959           out += this.renderer.del(this.output(cap[1]));
18960           continue;
18961         }
18962     
18963         // text
18964         if (cap = this.rules.text.exec(src)) {
18965           src = src.substring(cap[0].length);
18966           out += this.renderer.text(escape(this.smartypants(cap[0])));
18967           continue;
18968         }
18969     
18970         if (src) {
18971           throw new
18972             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18973         }
18974       }
18975     
18976       return out;
18977     };
18978     
18979     /**
18980      * Compile Link
18981      */
18982     
18983     InlineLexer.prototype.outputLink = function(cap, link) {
18984       var href = escape(link.href)
18985         , title = link.title ? escape(link.title) : null;
18986     
18987       return cap[0].charAt(0) !== '!'
18988         ? this.renderer.link(href, title, this.output(cap[1]))
18989         : this.renderer.image(href, title, escape(cap[1]));
18990     };
18991     
18992     /**
18993      * Smartypants Transformations
18994      */
18995     
18996     InlineLexer.prototype.smartypants = function(text) {
18997       if (!this.options.smartypants)  { return text; }
18998       return text
18999         // em-dashes
19000         .replace(/---/g, '\u2014')
19001         // en-dashes
19002         .replace(/--/g, '\u2013')
19003         // opening singles
19004         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19005         // closing singles & apostrophes
19006         .replace(/'/g, '\u2019')
19007         // opening doubles
19008         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19009         // closing doubles
19010         .replace(/"/g, '\u201d')
19011         // ellipses
19012         .replace(/\.{3}/g, '\u2026');
19013     };
19014     
19015     /**
19016      * Mangle Links
19017      */
19018     
19019     InlineLexer.prototype.mangle = function(text) {
19020       if (!this.options.mangle) { return text; }
19021       var out = ''
19022         , l = text.length
19023         , i = 0
19024         , ch;
19025     
19026       for (; i < l; i++) {
19027         ch = text.charCodeAt(i);
19028         if (Math.random() > 0.5) {
19029           ch = 'x' + ch.toString(16);
19030         }
19031         out += '&#' + ch + ';';
19032       }
19033     
19034       return out;
19035     };
19036     
19037     /**
19038      * Renderer
19039      */
19040     
19041      /**
19042          * eval:var:Renderer
19043     */
19044     
19045     var Renderer   = function (options) {
19046       this.options = options || {};
19047     }
19048     
19049     Renderer.prototype.code = function(code, lang, escaped) {
19050       if (this.options.highlight) {
19051         var out = this.options.highlight(code, lang);
19052         if (out != null && out !== code) {
19053           escaped = true;
19054           code = out;
19055         }
19056       } else {
19057             // hack!!! - it's already escapeD?
19058             escaped = true;
19059       }
19060     
19061       if (!lang) {
19062         return '<pre><code>'
19063           + (escaped ? code : escape(code, true))
19064           + '\n</code></pre>';
19065       }
19066     
19067       return '<pre><code class="'
19068         + this.options.langPrefix
19069         + escape(lang, true)
19070         + '">'
19071         + (escaped ? code : escape(code, true))
19072         + '\n</code></pre>\n';
19073     };
19074     
19075     Renderer.prototype.blockquote = function(quote) {
19076       return '<blockquote>\n' + quote + '</blockquote>\n';
19077     };
19078     
19079     Renderer.prototype.html = function(html) {
19080       return html;
19081     };
19082     
19083     Renderer.prototype.heading = function(text, level, raw) {
19084       return '<h'
19085         + level
19086         + ' id="'
19087         + this.options.headerPrefix
19088         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19089         + '">'
19090         + text
19091         + '</h'
19092         + level
19093         + '>\n';
19094     };
19095     
19096     Renderer.prototype.hr = function() {
19097       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19098     };
19099     
19100     Renderer.prototype.list = function(body, ordered) {
19101       var type = ordered ? 'ol' : 'ul';
19102       return '<' + type + '>\n' + body + '</' + type + '>\n';
19103     };
19104     
19105     Renderer.prototype.listitem = function(text) {
19106       return '<li>' + text + '</li>\n';
19107     };
19108     
19109     Renderer.prototype.paragraph = function(text) {
19110       return '<p>' + text + '</p>\n';
19111     };
19112     
19113     Renderer.prototype.table = function(header, body) {
19114       return '<table class="table table-striped">\n'
19115         + '<thead>\n'
19116         + header
19117         + '</thead>\n'
19118         + '<tbody>\n'
19119         + body
19120         + '</tbody>\n'
19121         + '</table>\n';
19122     };
19123     
19124     Renderer.prototype.tablerow = function(content) {
19125       return '<tr>\n' + content + '</tr>\n';
19126     };
19127     
19128     Renderer.prototype.tablecell = function(content, flags) {
19129       var type = flags.header ? 'th' : 'td';
19130       var tag = flags.align
19131         ? '<' + type + ' style="text-align:' + flags.align + '">'
19132         : '<' + type + '>';
19133       return tag + content + '</' + type + '>\n';
19134     };
19135     
19136     // span level renderer
19137     Renderer.prototype.strong = function(text) {
19138       return '<strong>' + text + '</strong>';
19139     };
19140     
19141     Renderer.prototype.em = function(text) {
19142       return '<em>' + text + '</em>';
19143     };
19144     
19145     Renderer.prototype.codespan = function(text) {
19146       return '<code>' + text + '</code>';
19147     };
19148     
19149     Renderer.prototype.br = function() {
19150       return this.options.xhtml ? '<br/>' : '<br>';
19151     };
19152     
19153     Renderer.prototype.del = function(text) {
19154       return '<del>' + text + '</del>';
19155     };
19156     
19157     Renderer.prototype.link = function(href, title, text) {
19158       if (this.options.sanitize) {
19159         try {
19160           var prot = decodeURIComponent(unescape(href))
19161             .replace(/[^\w:]/g, '')
19162             .toLowerCase();
19163         } catch (e) {
19164           return '';
19165         }
19166         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19167           return '';
19168         }
19169       }
19170       var out = '<a href="' + href + '"';
19171       if (title) {
19172         out += ' title="' + title + '"';
19173       }
19174       out += '>' + text + '</a>';
19175       return out;
19176     };
19177     
19178     Renderer.prototype.image = function(href, title, text) {
19179       var out = '<img src="' + href + '" alt="' + text + '"';
19180       if (title) {
19181         out += ' title="' + title + '"';
19182       }
19183       out += this.options.xhtml ? '/>' : '>';
19184       return out;
19185     };
19186     
19187     Renderer.prototype.text = function(text) {
19188       return text;
19189     };
19190     
19191     /**
19192      * Parsing & Compiling
19193      */
19194          /**
19195          * eval:var:Parser
19196     */
19197     
19198     var Parser= function (options) {
19199       this.tokens = [];
19200       this.token = null;
19201       this.options = options || marked.defaults;
19202       this.options.renderer = this.options.renderer || new Renderer;
19203       this.renderer = this.options.renderer;
19204       this.renderer.options = this.options;
19205     }
19206     
19207     /**
19208      * Static Parse Method
19209      */
19210     
19211     Parser.parse = function(src, options, renderer) {
19212       var parser = new Parser(options, renderer);
19213       return parser.parse(src);
19214     };
19215     
19216     /**
19217      * Parse Loop
19218      */
19219     
19220     Parser.prototype.parse = function(src) {
19221       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19222       this.tokens = src.reverse();
19223     
19224       var out = '';
19225       while (this.next()) {
19226         out += this.tok();
19227       }
19228     
19229       return out;
19230     };
19231     
19232     /**
19233      * Next Token
19234      */
19235     
19236     Parser.prototype.next = function() {
19237       return this.token = this.tokens.pop();
19238     };
19239     
19240     /**
19241      * Preview Next Token
19242      */
19243     
19244     Parser.prototype.peek = function() {
19245       return this.tokens[this.tokens.length - 1] || 0;
19246     };
19247     
19248     /**
19249      * Parse Text Tokens
19250      */
19251     
19252     Parser.prototype.parseText = function() {
19253       var body = this.token.text;
19254     
19255       while (this.peek().type === 'text') {
19256         body += '\n' + this.next().text;
19257       }
19258     
19259       return this.inline.output(body);
19260     };
19261     
19262     /**
19263      * Parse Current Token
19264      */
19265     
19266     Parser.prototype.tok = function() {
19267       switch (this.token.type) {
19268         case 'space': {
19269           return '';
19270         }
19271         case 'hr': {
19272           return this.renderer.hr();
19273         }
19274         case 'heading': {
19275           return this.renderer.heading(
19276             this.inline.output(this.token.text),
19277             this.token.depth,
19278             this.token.text);
19279         }
19280         case 'code': {
19281           return this.renderer.code(this.token.text,
19282             this.token.lang,
19283             this.token.escaped);
19284         }
19285         case 'table': {
19286           var header = ''
19287             , body = ''
19288             , i
19289             , row
19290             , cell
19291             , flags
19292             , j;
19293     
19294           // header
19295           cell = '';
19296           for (i = 0; i < this.token.header.length; i++) {
19297             flags = { header: true, align: this.token.align[i] };
19298             cell += this.renderer.tablecell(
19299               this.inline.output(this.token.header[i]),
19300               { header: true, align: this.token.align[i] }
19301             );
19302           }
19303           header += this.renderer.tablerow(cell);
19304     
19305           for (i = 0; i < this.token.cells.length; i++) {
19306             row = this.token.cells[i];
19307     
19308             cell = '';
19309             for (j = 0; j < row.length; j++) {
19310               cell += this.renderer.tablecell(
19311                 this.inline.output(row[j]),
19312                 { header: false, align: this.token.align[j] }
19313               );
19314             }
19315     
19316             body += this.renderer.tablerow(cell);
19317           }
19318           return this.renderer.table(header, body);
19319         }
19320         case 'blockquote_start': {
19321           var body = '';
19322     
19323           while (this.next().type !== 'blockquote_end') {
19324             body += this.tok();
19325           }
19326     
19327           return this.renderer.blockquote(body);
19328         }
19329         case 'list_start': {
19330           var body = ''
19331             , ordered = this.token.ordered;
19332     
19333           while (this.next().type !== 'list_end') {
19334             body += this.tok();
19335           }
19336     
19337           return this.renderer.list(body, ordered);
19338         }
19339         case 'list_item_start': {
19340           var body = '';
19341     
19342           while (this.next().type !== 'list_item_end') {
19343             body += this.token.type === 'text'
19344               ? this.parseText()
19345               : this.tok();
19346           }
19347     
19348           return this.renderer.listitem(body);
19349         }
19350         case 'loose_item_start': {
19351           var body = '';
19352     
19353           while (this.next().type !== 'list_item_end') {
19354             body += this.tok();
19355           }
19356     
19357           return this.renderer.listitem(body);
19358         }
19359         case 'html': {
19360           var html = !this.token.pre && !this.options.pedantic
19361             ? this.inline.output(this.token.text)
19362             : this.token.text;
19363           return this.renderer.html(html);
19364         }
19365         case 'paragraph': {
19366           return this.renderer.paragraph(this.inline.output(this.token.text));
19367         }
19368         case 'text': {
19369           return this.renderer.paragraph(this.parseText());
19370         }
19371       }
19372     };
19373   
19374     
19375     /**
19376      * Marked
19377      */
19378          /**
19379          * eval:var:marked
19380     */
19381     var marked = function (src, opt, callback) {
19382       if (callback || typeof opt === 'function') {
19383         if (!callback) {
19384           callback = opt;
19385           opt = null;
19386         }
19387     
19388         opt = merge({}, marked.defaults, opt || {});
19389     
19390         var highlight = opt.highlight
19391           , tokens
19392           , pending
19393           , i = 0;
19394     
19395         try {
19396           tokens = Lexer.lex(src, opt)
19397         } catch (e) {
19398           return callback(e);
19399         }
19400     
19401         pending = tokens.length;
19402          /**
19403          * eval:var:done
19404     */
19405         var done = function(err) {
19406           if (err) {
19407             opt.highlight = highlight;
19408             return callback(err);
19409           }
19410     
19411           var out;
19412     
19413           try {
19414             out = Parser.parse(tokens, opt);
19415           } catch (e) {
19416             err = e;
19417           }
19418     
19419           opt.highlight = highlight;
19420     
19421           return err
19422             ? callback(err)
19423             : callback(null, out);
19424         };
19425     
19426         if (!highlight || highlight.length < 3) {
19427           return done();
19428         }
19429     
19430         delete opt.highlight;
19431     
19432         if (!pending) { return done(); }
19433     
19434         for (; i < tokens.length; i++) {
19435           (function(token) {
19436             if (token.type !== 'code') {
19437               return --pending || done();
19438             }
19439             return highlight(token.text, token.lang, function(err, code) {
19440               if (err) { return done(err); }
19441               if (code == null || code === token.text) {
19442                 return --pending || done();
19443               }
19444               token.text = code;
19445               token.escaped = true;
19446               --pending || done();
19447             });
19448           })(tokens[i]);
19449         }
19450     
19451         return;
19452       }
19453       try {
19454         if (opt) { opt = merge({}, marked.defaults, opt); }
19455         return Parser.parse(Lexer.lex(src, opt), opt);
19456       } catch (e) {
19457         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19458         if ((opt || marked.defaults).silent) {
19459           return '<p>An error occured:</p><pre>'
19460             + escape(e.message + '', true)
19461             + '</pre>';
19462         }
19463         throw e;
19464       }
19465     }
19466     
19467     /**
19468      * Options
19469      */
19470     
19471     marked.options =
19472     marked.setOptions = function(opt) {
19473       merge(marked.defaults, opt);
19474       return marked;
19475     };
19476     
19477     marked.defaults = {
19478       gfm: true,
19479       tables: true,
19480       breaks: false,
19481       pedantic: false,
19482       sanitize: false,
19483       sanitizer: null,
19484       mangle: true,
19485       smartLists: false,
19486       silent: false,
19487       highlight: null,
19488       langPrefix: 'lang-',
19489       smartypants: false,
19490       headerPrefix: '',
19491       renderer: new Renderer,
19492       xhtml: false
19493     };
19494     
19495     /**
19496      * Expose
19497      */
19498     
19499     marked.Parser = Parser;
19500     marked.parser = Parser.parse;
19501     
19502     marked.Renderer = Renderer;
19503     
19504     marked.Lexer = Lexer;
19505     marked.lexer = Lexer.lex;
19506     
19507     marked.InlineLexer = InlineLexer;
19508     marked.inlineLexer = InlineLexer.output;
19509     
19510     marked.parse = marked;
19511     
19512     Roo.Markdown.marked = marked;
19513
19514 })();/*
19515  * Based on:
19516  * Ext JS Library 1.1.1
19517  * Copyright(c) 2006-2007, Ext JS, LLC.
19518  *
19519  * Originally Released Under LGPL - original licence link has changed is not relivant.
19520  *
19521  * Fork - LGPL
19522  * <script type="text/javascript">
19523  */
19524
19525
19526
19527 /*
19528  * These classes are derivatives of the similarly named classes in the YUI Library.
19529  * The original license:
19530  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19531  * Code licensed under the BSD License:
19532  * http://developer.yahoo.net/yui/license.txt
19533  */
19534
19535 (function() {
19536
19537 var Event=Roo.EventManager;
19538 var Dom=Roo.lib.Dom;
19539
19540 /**
19541  * @class Roo.dd.DragDrop
19542  * @extends Roo.util.Observable
19543  * Defines the interface and base operation of items that that can be
19544  * dragged or can be drop targets.  It was designed to be extended, overriding
19545  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19546  * Up to three html elements can be associated with a DragDrop instance:
19547  * <ul>
19548  * <li>linked element: the element that is passed into the constructor.
19549  * This is the element which defines the boundaries for interaction with
19550  * other DragDrop objects.</li>
19551  * <li>handle element(s): The drag operation only occurs if the element that
19552  * was clicked matches a handle element.  By default this is the linked
19553  * element, but there are times that you will want only a portion of the
19554  * linked element to initiate the drag operation, and the setHandleElId()
19555  * method provides a way to define this.</li>
19556  * <li>drag element: this represents the element that would be moved along
19557  * with the cursor during a drag operation.  By default, this is the linked
19558  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19559  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19560  * </li>
19561  * </ul>
19562  * This class should not be instantiated until the onload event to ensure that
19563  * the associated elements are available.
19564  * The following would define a DragDrop obj that would interact with any
19565  * other DragDrop obj in the "group1" group:
19566  * <pre>
19567  *  dd = new Roo.dd.DragDrop("div1", "group1");
19568  * </pre>
19569  * Since none of the event handlers have been implemented, nothing would
19570  * actually happen if you were to run the code above.  Normally you would
19571  * override this class or one of the default implementations, but you can
19572  * also override the methods you want on an instance of the class...
19573  * <pre>
19574  *  dd.onDragDrop = function(e, id) {
19575  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19576  *  }
19577  * </pre>
19578  * @constructor
19579  * @param {String} id of the element that is linked to this instance
19580  * @param {String} sGroup the group of related DragDrop objects
19581  * @param {object} config an object containing configurable attributes
19582  *                Valid properties for DragDrop:
19583  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19584  */
19585 Roo.dd.DragDrop = function(id, sGroup, config) {
19586     if (id) {
19587         this.init(id, sGroup, config);
19588     }
19589     
19590 };
19591
19592 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19593
19594     /**
19595      * The id of the element associated with this object.  This is what we
19596      * refer to as the "linked element" because the size and position of
19597      * this element is used to determine when the drag and drop objects have
19598      * interacted.
19599      * @property id
19600      * @type String
19601      */
19602     id: null,
19603
19604     /**
19605      * Configuration attributes passed into the constructor
19606      * @property config
19607      * @type object
19608      */
19609     config: null,
19610
19611     /**
19612      * The id of the element that will be dragged.  By default this is same
19613      * as the linked element , but could be changed to another element. Ex:
19614      * Roo.dd.DDProxy
19615      * @property dragElId
19616      * @type String
19617      * @private
19618      */
19619     dragElId: null,
19620
19621     /**
19622      * the id of the element that initiates the drag operation.  By default
19623      * this is the linked element, but could be changed to be a child of this
19624      * element.  This lets us do things like only starting the drag when the
19625      * header element within the linked html element is clicked.
19626      * @property handleElId
19627      * @type String
19628      * @private
19629      */
19630     handleElId: null,
19631
19632     /**
19633      * An associative array of HTML tags that will be ignored if clicked.
19634      * @property invalidHandleTypes
19635      * @type {string: string}
19636      */
19637     invalidHandleTypes: null,
19638
19639     /**
19640      * An associative array of ids for elements that will be ignored if clicked
19641      * @property invalidHandleIds
19642      * @type {string: string}
19643      */
19644     invalidHandleIds: null,
19645
19646     /**
19647      * An indexted array of css class names for elements that will be ignored
19648      * if clicked.
19649      * @property invalidHandleClasses
19650      * @type string[]
19651      */
19652     invalidHandleClasses: null,
19653
19654     /**
19655      * The linked element's absolute X position at the time the drag was
19656      * started
19657      * @property startPageX
19658      * @type int
19659      * @private
19660      */
19661     startPageX: 0,
19662
19663     /**
19664      * The linked element's absolute X position at the time the drag was
19665      * started
19666      * @property startPageY
19667      * @type int
19668      * @private
19669      */
19670     startPageY: 0,
19671
19672     /**
19673      * The group defines a logical collection of DragDrop objects that are
19674      * related.  Instances only get events when interacting with other
19675      * DragDrop object in the same group.  This lets us define multiple
19676      * groups using a single DragDrop subclass if we want.
19677      * @property groups
19678      * @type {string: string}
19679      */
19680     groups: null,
19681
19682     /**
19683      * Individual drag/drop instances can be locked.  This will prevent
19684      * onmousedown start drag.
19685      * @property locked
19686      * @type boolean
19687      * @private
19688      */
19689     locked: false,
19690
19691     /**
19692      * Lock this instance
19693      * @method lock
19694      */
19695     lock: function() { this.locked = true; },
19696
19697     /**
19698      * Unlock this instace
19699      * @method unlock
19700      */
19701     unlock: function() { this.locked = false; },
19702
19703     /**
19704      * By default, all insances can be a drop target.  This can be disabled by
19705      * setting isTarget to false.
19706      * @method isTarget
19707      * @type boolean
19708      */
19709     isTarget: true,
19710
19711     /**
19712      * The padding configured for this drag and drop object for calculating
19713      * the drop zone intersection with this object.
19714      * @method padding
19715      * @type int[]
19716      */
19717     padding: null,
19718
19719     /**
19720      * Cached reference to the linked element
19721      * @property _domRef
19722      * @private
19723      */
19724     _domRef: null,
19725
19726     /**
19727      * Internal typeof flag
19728      * @property __ygDragDrop
19729      * @private
19730      */
19731     __ygDragDrop: true,
19732
19733     /**
19734      * Set to true when horizontal contraints are applied
19735      * @property constrainX
19736      * @type boolean
19737      * @private
19738      */
19739     constrainX: false,
19740
19741     /**
19742      * Set to true when vertical contraints are applied
19743      * @property constrainY
19744      * @type boolean
19745      * @private
19746      */
19747     constrainY: false,
19748
19749     /**
19750      * The left constraint
19751      * @property minX
19752      * @type int
19753      * @private
19754      */
19755     minX: 0,
19756
19757     /**
19758      * The right constraint
19759      * @property maxX
19760      * @type int
19761      * @private
19762      */
19763     maxX: 0,
19764
19765     /**
19766      * The up constraint
19767      * @property minY
19768      * @type int
19769      * @type int
19770      * @private
19771      */
19772     minY: 0,
19773
19774     /**
19775      * The down constraint
19776      * @property maxY
19777      * @type int
19778      * @private
19779      */
19780     maxY: 0,
19781
19782     /**
19783      * Maintain offsets when we resetconstraints.  Set to true when you want
19784      * the position of the element relative to its parent to stay the same
19785      * when the page changes
19786      *
19787      * @property maintainOffset
19788      * @type boolean
19789      */
19790     maintainOffset: false,
19791
19792     /**
19793      * Array of pixel locations the element will snap to if we specified a
19794      * horizontal graduation/interval.  This array is generated automatically
19795      * when you define a tick interval.
19796      * @property xTicks
19797      * @type int[]
19798      */
19799     xTicks: null,
19800
19801     /**
19802      * Array of pixel locations the element will snap to if we specified a
19803      * vertical graduation/interval.  This array is generated automatically
19804      * when you define a tick interval.
19805      * @property yTicks
19806      * @type int[]
19807      */
19808     yTicks: null,
19809
19810     /**
19811      * By default the drag and drop instance will only respond to the primary
19812      * button click (left button for a right-handed mouse).  Set to true to
19813      * allow drag and drop to start with any mouse click that is propogated
19814      * by the browser
19815      * @property primaryButtonOnly
19816      * @type boolean
19817      */
19818     primaryButtonOnly: true,
19819
19820     /**
19821      * The availabe property is false until the linked dom element is accessible.
19822      * @property available
19823      * @type boolean
19824      */
19825     available: false,
19826
19827     /**
19828      * By default, drags can only be initiated if the mousedown occurs in the
19829      * region the linked element is.  This is done in part to work around a
19830      * bug in some browsers that mis-report the mousedown if the previous
19831      * mouseup happened outside of the window.  This property is set to true
19832      * if outer handles are defined.
19833      *
19834      * @property hasOuterHandles
19835      * @type boolean
19836      * @default false
19837      */
19838     hasOuterHandles: false,
19839
19840     /**
19841      * Code that executes immediately before the startDrag event
19842      * @method b4StartDrag
19843      * @private
19844      */
19845     b4StartDrag: function(x, y) { },
19846
19847     /**
19848      * Abstract method called after a drag/drop object is clicked
19849      * and the drag or mousedown time thresholds have beeen met.
19850      * @method startDrag
19851      * @param {int} X click location
19852      * @param {int} Y click location
19853      */
19854     startDrag: function(x, y) { /* override this */ },
19855
19856     /**
19857      * Code that executes immediately before the onDrag event
19858      * @method b4Drag
19859      * @private
19860      */
19861     b4Drag: function(e) { },
19862
19863     /**
19864      * Abstract method called during the onMouseMove event while dragging an
19865      * object.
19866      * @method onDrag
19867      * @param {Event} e the mousemove event
19868      */
19869     onDrag: function(e) { /* override this */ },
19870
19871     /**
19872      * Abstract method called when this element fist begins hovering over
19873      * another DragDrop obj
19874      * @method onDragEnter
19875      * @param {Event} e the mousemove event
19876      * @param {String|DragDrop[]} id In POINT mode, the element
19877      * id this is hovering over.  In INTERSECT mode, an array of one or more
19878      * dragdrop items being hovered over.
19879      */
19880     onDragEnter: function(e, id) { /* override this */ },
19881
19882     /**
19883      * Code that executes immediately before the onDragOver event
19884      * @method b4DragOver
19885      * @private
19886      */
19887     b4DragOver: function(e) { },
19888
19889     /**
19890      * Abstract method called when this element is hovering over another
19891      * DragDrop obj
19892      * @method onDragOver
19893      * @param {Event} e the mousemove event
19894      * @param {String|DragDrop[]} id In POINT mode, the element
19895      * id this is hovering over.  In INTERSECT mode, an array of dd items
19896      * being hovered over.
19897      */
19898     onDragOver: function(e, id) { /* override this */ },
19899
19900     /**
19901      * Code that executes immediately before the onDragOut event
19902      * @method b4DragOut
19903      * @private
19904      */
19905     b4DragOut: function(e) { },
19906
19907     /**
19908      * Abstract method called when we are no longer hovering over an element
19909      * @method onDragOut
19910      * @param {Event} e the mousemove event
19911      * @param {String|DragDrop[]} id In POINT mode, the element
19912      * id this was hovering over.  In INTERSECT mode, an array of dd items
19913      * that the mouse is no longer over.
19914      */
19915     onDragOut: function(e, id) { /* override this */ },
19916
19917     /**
19918      * Code that executes immediately before the onDragDrop event
19919      * @method b4DragDrop
19920      * @private
19921      */
19922     b4DragDrop: function(e) { },
19923
19924     /**
19925      * Abstract method called when this item is dropped on another DragDrop
19926      * obj
19927      * @method onDragDrop
19928      * @param {Event} e the mouseup event
19929      * @param {String|DragDrop[]} id In POINT mode, the element
19930      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19931      * was dropped on.
19932      */
19933     onDragDrop: function(e, id) { /* override this */ },
19934
19935     /**
19936      * Abstract method called when this item is dropped on an area with no
19937      * drop target
19938      * @method onInvalidDrop
19939      * @param {Event} e the mouseup event
19940      */
19941     onInvalidDrop: function(e) { /* override this */ },
19942
19943     /**
19944      * Code that executes immediately before the endDrag event
19945      * @method b4EndDrag
19946      * @private
19947      */
19948     b4EndDrag: function(e) { },
19949
19950     /**
19951      * Fired when we are done dragging the object
19952      * @method endDrag
19953      * @param {Event} e the mouseup event
19954      */
19955     endDrag: function(e) { /* override this */ },
19956
19957     /**
19958      * Code executed immediately before the onMouseDown event
19959      * @method b4MouseDown
19960      * @param {Event} e the mousedown event
19961      * @private
19962      */
19963     b4MouseDown: function(e) {  },
19964
19965     /**
19966      * Event handler that fires when a drag/drop obj gets a mousedown
19967      * @method onMouseDown
19968      * @param {Event} e the mousedown event
19969      */
19970     onMouseDown: function(e) { /* override this */ },
19971
19972     /**
19973      * Event handler that fires when a drag/drop obj gets a mouseup
19974      * @method onMouseUp
19975      * @param {Event} e the mouseup event
19976      */
19977     onMouseUp: function(e) { /* override this */ },
19978
19979     /**
19980      * Override the onAvailable method to do what is needed after the initial
19981      * position was determined.
19982      * @method onAvailable
19983      */
19984     onAvailable: function () {
19985     },
19986
19987     /*
19988      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19989      * @type Object
19990      */
19991     defaultPadding : {left:0, right:0, top:0, bottom:0},
19992
19993     /*
19994      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19995  *
19996  * Usage:
19997  <pre><code>
19998  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19999                 { dragElId: "existingProxyDiv" });
20000  dd.startDrag = function(){
20001      this.constrainTo("parent-id");
20002  };
20003  </code></pre>
20004  * Or you can initalize it using the {@link Roo.Element} object:
20005  <pre><code>
20006  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20007      startDrag : function(){
20008          this.constrainTo("parent-id");
20009      }
20010  });
20011  </code></pre>
20012      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20013      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20014      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20015      * an object containing the sides to pad. For example: {right:10, bottom:10}
20016      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20017      */
20018     constrainTo : function(constrainTo, pad, inContent){
20019         if(typeof pad == "number"){
20020             pad = {left: pad, right:pad, top:pad, bottom:pad};
20021         }
20022         pad = pad || this.defaultPadding;
20023         var b = Roo.get(this.getEl()).getBox();
20024         var ce = Roo.get(constrainTo);
20025         var s = ce.getScroll();
20026         var c, cd = ce.dom;
20027         if(cd == document.body){
20028             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20029         }else{
20030             xy = ce.getXY();
20031             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20032         }
20033
20034
20035         var topSpace = b.y - c.y;
20036         var leftSpace = b.x - c.x;
20037
20038         this.resetConstraints();
20039         this.setXConstraint(leftSpace - (pad.left||0), // left
20040                 c.width - leftSpace - b.width - (pad.right||0) //right
20041         );
20042         this.setYConstraint(topSpace - (pad.top||0), //top
20043                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20044         );
20045     },
20046
20047     /**
20048      * Returns a reference to the linked element
20049      * @method getEl
20050      * @return {HTMLElement} the html element
20051      */
20052     getEl: function() {
20053         if (!this._domRef) {
20054             this._domRef = Roo.getDom(this.id);
20055         }
20056
20057         return this._domRef;
20058     },
20059
20060     /**
20061      * Returns a reference to the actual element to drag.  By default this is
20062      * the same as the html element, but it can be assigned to another
20063      * element. An example of this can be found in Roo.dd.DDProxy
20064      * @method getDragEl
20065      * @return {HTMLElement} the html element
20066      */
20067     getDragEl: function() {
20068         return Roo.getDom(this.dragElId);
20069     },
20070
20071     /**
20072      * Sets up the DragDrop object.  Must be called in the constructor of any
20073      * Roo.dd.DragDrop subclass
20074      * @method init
20075      * @param id the id of the linked element
20076      * @param {String} sGroup the group of related items
20077      * @param {object} config configuration attributes
20078      */
20079     init: function(id, sGroup, config) {
20080         this.initTarget(id, sGroup, config);
20081         if (!Roo.isTouch) {
20082             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20083         }
20084         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20085         // Event.on(this.id, "selectstart", Event.preventDefault);
20086     },
20087
20088     /**
20089      * Initializes Targeting functionality only... the object does not
20090      * get a mousedown handler.
20091      * @method initTarget
20092      * @param id the id of the linked element
20093      * @param {String} sGroup the group of related items
20094      * @param {object} config configuration attributes
20095      */
20096     initTarget: function(id, sGroup, config) {
20097
20098         // configuration attributes
20099         this.config = config || {};
20100
20101         // create a local reference to the drag and drop manager
20102         this.DDM = Roo.dd.DDM;
20103         // initialize the groups array
20104         this.groups = {};
20105
20106         // assume that we have an element reference instead of an id if the
20107         // parameter is not a string
20108         if (typeof id !== "string") {
20109             id = Roo.id(id);
20110         }
20111
20112         // set the id
20113         this.id = id;
20114
20115         // add to an interaction group
20116         this.addToGroup((sGroup) ? sGroup : "default");
20117
20118         // We don't want to register this as the handle with the manager
20119         // so we just set the id rather than calling the setter.
20120         this.handleElId = id;
20121
20122         // the linked element is the element that gets dragged by default
20123         this.setDragElId(id);
20124
20125         // by default, clicked anchors will not start drag operations.
20126         this.invalidHandleTypes = { A: "A" };
20127         this.invalidHandleIds = {};
20128         this.invalidHandleClasses = [];
20129
20130         this.applyConfig();
20131
20132         this.handleOnAvailable();
20133     },
20134
20135     /**
20136      * Applies the configuration parameters that were passed into the constructor.
20137      * This is supposed to happen at each level through the inheritance chain.  So
20138      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20139      * DragDrop in order to get all of the parameters that are available in
20140      * each object.
20141      * @method applyConfig
20142      */
20143     applyConfig: function() {
20144
20145         // configurable properties:
20146         //    padding, isTarget, maintainOffset, primaryButtonOnly
20147         this.padding           = this.config.padding || [0, 0, 0, 0];
20148         this.isTarget          = (this.config.isTarget !== false);
20149         this.maintainOffset    = (this.config.maintainOffset);
20150         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20151
20152     },
20153
20154     /**
20155      * Executed when the linked element is available
20156      * @method handleOnAvailable
20157      * @private
20158      */
20159     handleOnAvailable: function() {
20160         this.available = true;
20161         this.resetConstraints();
20162         this.onAvailable();
20163     },
20164
20165      /**
20166      * Configures the padding for the target zone in px.  Effectively expands
20167      * (or reduces) the virtual object size for targeting calculations.
20168      * Supports css-style shorthand; if only one parameter is passed, all sides
20169      * will have that padding, and if only two are passed, the top and bottom
20170      * will have the first param, the left and right the second.
20171      * @method setPadding
20172      * @param {int} iTop    Top pad
20173      * @param {int} iRight  Right pad
20174      * @param {int} iBot    Bot pad
20175      * @param {int} iLeft   Left pad
20176      */
20177     setPadding: function(iTop, iRight, iBot, iLeft) {
20178         // this.padding = [iLeft, iRight, iTop, iBot];
20179         if (!iRight && 0 !== iRight) {
20180             this.padding = [iTop, iTop, iTop, iTop];
20181         } else if (!iBot && 0 !== iBot) {
20182             this.padding = [iTop, iRight, iTop, iRight];
20183         } else {
20184             this.padding = [iTop, iRight, iBot, iLeft];
20185         }
20186     },
20187
20188     /**
20189      * Stores the initial placement of the linked element.
20190      * @method setInitialPosition
20191      * @param {int} diffX   the X offset, default 0
20192      * @param {int} diffY   the Y offset, default 0
20193      */
20194     setInitPosition: function(diffX, diffY) {
20195         var el = this.getEl();
20196
20197         if (!this.DDM.verifyEl(el)) {
20198             return;
20199         }
20200
20201         var dx = diffX || 0;
20202         var dy = diffY || 0;
20203
20204         var p = Dom.getXY( el );
20205
20206         this.initPageX = p[0] - dx;
20207         this.initPageY = p[1] - dy;
20208
20209         this.lastPageX = p[0];
20210         this.lastPageY = p[1];
20211
20212
20213         this.setStartPosition(p);
20214     },
20215
20216     /**
20217      * Sets the start position of the element.  This is set when the obj
20218      * is initialized, the reset when a drag is started.
20219      * @method setStartPosition
20220      * @param pos current position (from previous lookup)
20221      * @private
20222      */
20223     setStartPosition: function(pos) {
20224         var p = pos || Dom.getXY( this.getEl() );
20225         this.deltaSetXY = null;
20226
20227         this.startPageX = p[0];
20228         this.startPageY = p[1];
20229     },
20230
20231     /**
20232      * Add this instance to a group of related drag/drop objects.  All
20233      * instances belong to at least one group, and can belong to as many
20234      * groups as needed.
20235      * @method addToGroup
20236      * @param sGroup {string} the name of the group
20237      */
20238     addToGroup: function(sGroup) {
20239         this.groups[sGroup] = true;
20240         this.DDM.regDragDrop(this, sGroup);
20241     },
20242
20243     /**
20244      * Remove's this instance from the supplied interaction group
20245      * @method removeFromGroup
20246      * @param {string}  sGroup  The group to drop
20247      */
20248     removeFromGroup: function(sGroup) {
20249         if (this.groups[sGroup]) {
20250             delete this.groups[sGroup];
20251         }
20252
20253         this.DDM.removeDDFromGroup(this, sGroup);
20254     },
20255
20256     /**
20257      * Allows you to specify that an element other than the linked element
20258      * will be moved with the cursor during a drag
20259      * @method setDragElId
20260      * @param id {string} the id of the element that will be used to initiate the drag
20261      */
20262     setDragElId: function(id) {
20263         this.dragElId = id;
20264     },
20265
20266     /**
20267      * Allows you to specify a child of the linked element that should be
20268      * used to initiate the drag operation.  An example of this would be if
20269      * you have a content div with text and links.  Clicking anywhere in the
20270      * content area would normally start the drag operation.  Use this method
20271      * to specify that an element inside of the content div is the element
20272      * that starts the drag operation.
20273      * @method setHandleElId
20274      * @param id {string} the id of the element that will be used to
20275      * initiate the drag.
20276      */
20277     setHandleElId: function(id) {
20278         if (typeof id !== "string") {
20279             id = Roo.id(id);
20280         }
20281         this.handleElId = id;
20282         this.DDM.regHandle(this.id, id);
20283     },
20284
20285     /**
20286      * Allows you to set an element outside of the linked element as a drag
20287      * handle
20288      * @method setOuterHandleElId
20289      * @param id the id of the element that will be used to initiate the drag
20290      */
20291     setOuterHandleElId: function(id) {
20292         if (typeof id !== "string") {
20293             id = Roo.id(id);
20294         }
20295         Event.on(id, "mousedown",
20296                 this.handleMouseDown, this);
20297         this.setHandleElId(id);
20298
20299         this.hasOuterHandles = true;
20300     },
20301
20302     /**
20303      * Remove all drag and drop hooks for this element
20304      * @method unreg
20305      */
20306     unreg: function() {
20307         Event.un(this.id, "mousedown",
20308                 this.handleMouseDown);
20309         Event.un(this.id, "touchstart",
20310                 this.handleMouseDown);
20311         this._domRef = null;
20312         this.DDM._remove(this);
20313     },
20314
20315     destroy : function(){
20316         this.unreg();
20317     },
20318
20319     /**
20320      * Returns true if this instance is locked, or the drag drop mgr is locked
20321      * (meaning that all drag/drop is disabled on the page.)
20322      * @method isLocked
20323      * @return {boolean} true if this obj or all drag/drop is locked, else
20324      * false
20325      */
20326     isLocked: function() {
20327         return (this.DDM.isLocked() || this.locked);
20328     },
20329
20330     /**
20331      * Fired when this object is clicked
20332      * @method handleMouseDown
20333      * @param {Event} e
20334      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20335      * @private
20336      */
20337     handleMouseDown: function(e, oDD){
20338      
20339         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20340             //Roo.log('not touch/ button !=0');
20341             return;
20342         }
20343         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20344             return; // double touch..
20345         }
20346         
20347
20348         if (this.isLocked()) {
20349             //Roo.log('locked');
20350             return;
20351         }
20352
20353         this.DDM.refreshCache(this.groups);
20354 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20355         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20356         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20357             //Roo.log('no outer handes or not over target');
20358                 // do nothing.
20359         } else {
20360 //            Roo.log('check validator');
20361             if (this.clickValidator(e)) {
20362 //                Roo.log('validate success');
20363                 // set the initial element position
20364                 this.setStartPosition();
20365
20366
20367                 this.b4MouseDown(e);
20368                 this.onMouseDown(e);
20369
20370                 this.DDM.handleMouseDown(e, this);
20371
20372                 this.DDM.stopEvent(e);
20373             } else {
20374
20375
20376             }
20377         }
20378     },
20379
20380     clickValidator: function(e) {
20381         var target = e.getTarget();
20382         return ( this.isValidHandleChild(target) &&
20383                     (this.id == this.handleElId ||
20384                         this.DDM.handleWasClicked(target, this.id)) );
20385     },
20386
20387     /**
20388      * Allows you to specify a tag name that should not start a drag operation
20389      * when clicked.  This is designed to facilitate embedding links within a
20390      * drag handle that do something other than start the drag.
20391      * @method addInvalidHandleType
20392      * @param {string} tagName the type of element to exclude
20393      */
20394     addInvalidHandleType: function(tagName) {
20395         var type = tagName.toUpperCase();
20396         this.invalidHandleTypes[type] = type;
20397     },
20398
20399     /**
20400      * Lets you to specify an element id for a child of a drag handle
20401      * that should not initiate a drag
20402      * @method addInvalidHandleId
20403      * @param {string} id the element id of the element you wish to ignore
20404      */
20405     addInvalidHandleId: function(id) {
20406         if (typeof id !== "string") {
20407             id = Roo.id(id);
20408         }
20409         this.invalidHandleIds[id] = id;
20410     },
20411
20412     /**
20413      * Lets you specify a css class of elements that will not initiate a drag
20414      * @method addInvalidHandleClass
20415      * @param {string} cssClass the class of the elements you wish to ignore
20416      */
20417     addInvalidHandleClass: function(cssClass) {
20418         this.invalidHandleClasses.push(cssClass);
20419     },
20420
20421     /**
20422      * Unsets an excluded tag name set by addInvalidHandleType
20423      * @method removeInvalidHandleType
20424      * @param {string} tagName the type of element to unexclude
20425      */
20426     removeInvalidHandleType: function(tagName) {
20427         var type = tagName.toUpperCase();
20428         // this.invalidHandleTypes[type] = null;
20429         delete this.invalidHandleTypes[type];
20430     },
20431
20432     /**
20433      * Unsets an invalid handle id
20434      * @method removeInvalidHandleId
20435      * @param {string} id the id of the element to re-enable
20436      */
20437     removeInvalidHandleId: function(id) {
20438         if (typeof id !== "string") {
20439             id = Roo.id(id);
20440         }
20441         delete this.invalidHandleIds[id];
20442     },
20443
20444     /**
20445      * Unsets an invalid css class
20446      * @method removeInvalidHandleClass
20447      * @param {string} cssClass the class of the element(s) you wish to
20448      * re-enable
20449      */
20450     removeInvalidHandleClass: function(cssClass) {
20451         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20452             if (this.invalidHandleClasses[i] == cssClass) {
20453                 delete this.invalidHandleClasses[i];
20454             }
20455         }
20456     },
20457
20458     /**
20459      * Checks the tag exclusion list to see if this click should be ignored
20460      * @method isValidHandleChild
20461      * @param {HTMLElement} node the HTMLElement to evaluate
20462      * @return {boolean} true if this is a valid tag type, false if not
20463      */
20464     isValidHandleChild: function(node) {
20465
20466         var valid = true;
20467         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20468         var nodeName;
20469         try {
20470             nodeName = node.nodeName.toUpperCase();
20471         } catch(e) {
20472             nodeName = node.nodeName;
20473         }
20474         valid = valid && !this.invalidHandleTypes[nodeName];
20475         valid = valid && !this.invalidHandleIds[node.id];
20476
20477         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20478             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20479         }
20480
20481
20482         return valid;
20483
20484     },
20485
20486     /**
20487      * Create the array of horizontal tick marks if an interval was specified
20488      * in setXConstraint().
20489      * @method setXTicks
20490      * @private
20491      */
20492     setXTicks: function(iStartX, iTickSize) {
20493         this.xTicks = [];
20494         this.xTickSize = iTickSize;
20495
20496         var tickMap = {};
20497
20498         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20499             if (!tickMap[i]) {
20500                 this.xTicks[this.xTicks.length] = i;
20501                 tickMap[i] = true;
20502             }
20503         }
20504
20505         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20506             if (!tickMap[i]) {
20507                 this.xTicks[this.xTicks.length] = i;
20508                 tickMap[i] = true;
20509             }
20510         }
20511
20512         this.xTicks.sort(this.DDM.numericSort) ;
20513     },
20514
20515     /**
20516      * Create the array of vertical tick marks if an interval was specified in
20517      * setYConstraint().
20518      * @method setYTicks
20519      * @private
20520      */
20521     setYTicks: function(iStartY, iTickSize) {
20522         this.yTicks = [];
20523         this.yTickSize = iTickSize;
20524
20525         var tickMap = {};
20526
20527         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20528             if (!tickMap[i]) {
20529                 this.yTicks[this.yTicks.length] = i;
20530                 tickMap[i] = true;
20531             }
20532         }
20533
20534         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20535             if (!tickMap[i]) {
20536                 this.yTicks[this.yTicks.length] = i;
20537                 tickMap[i] = true;
20538             }
20539         }
20540
20541         this.yTicks.sort(this.DDM.numericSort) ;
20542     },
20543
20544     /**
20545      * By default, the element can be dragged any place on the screen.  Use
20546      * this method to limit the horizontal travel of the element.  Pass in
20547      * 0,0 for the parameters if you want to lock the drag to the y axis.
20548      * @method setXConstraint
20549      * @param {int} iLeft the number of pixels the element can move to the left
20550      * @param {int} iRight the number of pixels the element can move to the
20551      * right
20552      * @param {int} iTickSize optional parameter for specifying that the
20553      * element
20554      * should move iTickSize pixels at a time.
20555      */
20556     setXConstraint: function(iLeft, iRight, iTickSize) {
20557         this.leftConstraint = iLeft;
20558         this.rightConstraint = iRight;
20559
20560         this.minX = this.initPageX - iLeft;
20561         this.maxX = this.initPageX + iRight;
20562         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20563
20564         this.constrainX = true;
20565     },
20566
20567     /**
20568      * Clears any constraints applied to this instance.  Also clears ticks
20569      * since they can't exist independent of a constraint at this time.
20570      * @method clearConstraints
20571      */
20572     clearConstraints: function() {
20573         this.constrainX = false;
20574         this.constrainY = false;
20575         this.clearTicks();
20576     },
20577
20578     /**
20579      * Clears any tick interval defined for this instance
20580      * @method clearTicks
20581      */
20582     clearTicks: function() {
20583         this.xTicks = null;
20584         this.yTicks = null;
20585         this.xTickSize = 0;
20586         this.yTickSize = 0;
20587     },
20588
20589     /**
20590      * By default, the element can be dragged any place on the screen.  Set
20591      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20592      * parameters if you want to lock the drag to the x axis.
20593      * @method setYConstraint
20594      * @param {int} iUp the number of pixels the element can move up
20595      * @param {int} iDown the number of pixels the element can move down
20596      * @param {int} iTickSize optional parameter for specifying that the
20597      * element should move iTickSize pixels at a time.
20598      */
20599     setYConstraint: function(iUp, iDown, iTickSize) {
20600         this.topConstraint = iUp;
20601         this.bottomConstraint = iDown;
20602
20603         this.minY = this.initPageY - iUp;
20604         this.maxY = this.initPageY + iDown;
20605         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20606
20607         this.constrainY = true;
20608
20609     },
20610
20611     /**
20612      * resetConstraints must be called if you manually reposition a dd element.
20613      * @method resetConstraints
20614      * @param {boolean} maintainOffset
20615      */
20616     resetConstraints: function() {
20617
20618
20619         // Maintain offsets if necessary
20620         if (this.initPageX || this.initPageX === 0) {
20621             // figure out how much this thing has moved
20622             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20623             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20624
20625             this.setInitPosition(dx, dy);
20626
20627         // This is the first time we have detected the element's position
20628         } else {
20629             this.setInitPosition();
20630         }
20631
20632         if (this.constrainX) {
20633             this.setXConstraint( this.leftConstraint,
20634                                  this.rightConstraint,
20635                                  this.xTickSize        );
20636         }
20637
20638         if (this.constrainY) {
20639             this.setYConstraint( this.topConstraint,
20640                                  this.bottomConstraint,
20641                                  this.yTickSize         );
20642         }
20643     },
20644
20645     /**
20646      * Normally the drag element is moved pixel by pixel, but we can specify
20647      * that it move a number of pixels at a time.  This method resolves the
20648      * location when we have it set up like this.
20649      * @method getTick
20650      * @param {int} val where we want to place the object
20651      * @param {int[]} tickArray sorted array of valid points
20652      * @return {int} the closest tick
20653      * @private
20654      */
20655     getTick: function(val, tickArray) {
20656
20657         if (!tickArray) {
20658             // If tick interval is not defined, it is effectively 1 pixel,
20659             // so we return the value passed to us.
20660             return val;
20661         } else if (tickArray[0] >= val) {
20662             // The value is lower than the first tick, so we return the first
20663             // tick.
20664             return tickArray[0];
20665         } else {
20666             for (var i=0, len=tickArray.length; i<len; ++i) {
20667                 var next = i + 1;
20668                 if (tickArray[next] && tickArray[next] >= val) {
20669                     var diff1 = val - tickArray[i];
20670                     var diff2 = tickArray[next] - val;
20671                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20672                 }
20673             }
20674
20675             // The value is larger than the last tick, so we return the last
20676             // tick.
20677             return tickArray[tickArray.length - 1];
20678         }
20679     },
20680
20681     /**
20682      * toString method
20683      * @method toString
20684      * @return {string} string representation of the dd obj
20685      */
20686     toString: function() {
20687         return ("DragDrop " + this.id);
20688     }
20689
20690 });
20691
20692 })();
20693 /*
20694  * Based on:
20695  * Ext JS Library 1.1.1
20696  * Copyright(c) 2006-2007, Ext JS, LLC.
20697  *
20698  * Originally Released Under LGPL - original licence link has changed is not relivant.
20699  *
20700  * Fork - LGPL
20701  * <script type="text/javascript">
20702  */
20703
20704
20705 /**
20706  * The drag and drop utility provides a framework for building drag and drop
20707  * applications.  In addition to enabling drag and drop for specific elements,
20708  * the drag and drop elements are tracked by the manager class, and the
20709  * interactions between the various elements are tracked during the drag and
20710  * the implementing code is notified about these important moments.
20711  */
20712
20713 // Only load the library once.  Rewriting the manager class would orphan
20714 // existing drag and drop instances.
20715 if (!Roo.dd.DragDropMgr) {
20716
20717 /**
20718  * @class Roo.dd.DragDropMgr
20719  * DragDropMgr is a singleton that tracks the element interaction for
20720  * all DragDrop items in the window.  Generally, you will not call
20721  * this class directly, but it does have helper methods that could
20722  * be useful in your DragDrop implementations.
20723  * @static
20724  */
20725 Roo.dd.DragDropMgr = function() {
20726
20727     var Event = Roo.EventManager;
20728
20729     return {
20730
20731         /**
20732          * Two dimensional Array of registered DragDrop objects.  The first
20733          * dimension is the DragDrop item group, the second the DragDrop
20734          * object.
20735          * @property ids
20736          * @type {string: string}
20737          * @private
20738          * @static
20739          */
20740         ids: {},
20741
20742         /**
20743          * Array of element ids defined as drag handles.  Used to determine
20744          * if the element that generated the mousedown event is actually the
20745          * handle and not the html element itself.
20746          * @property handleIds
20747          * @type {string: string}
20748          * @private
20749          * @static
20750          */
20751         handleIds: {},
20752
20753         /**
20754          * the DragDrop object that is currently being dragged
20755          * @property dragCurrent
20756          * @type DragDrop
20757          * @private
20758          * @static
20759          **/
20760         dragCurrent: null,
20761
20762         /**
20763          * the DragDrop object(s) that are being hovered over
20764          * @property dragOvers
20765          * @type Array
20766          * @private
20767          * @static
20768          */
20769         dragOvers: {},
20770
20771         /**
20772          * the X distance between the cursor and the object being dragged
20773          * @property deltaX
20774          * @type int
20775          * @private
20776          * @static
20777          */
20778         deltaX: 0,
20779
20780         /**
20781          * the Y distance between the cursor and the object being dragged
20782          * @property deltaY
20783          * @type int
20784          * @private
20785          * @static
20786          */
20787         deltaY: 0,
20788
20789         /**
20790          * Flag to determine if we should prevent the default behavior of the
20791          * events we define. By default this is true, but this can be set to
20792          * false if you need the default behavior (not recommended)
20793          * @property preventDefault
20794          * @type boolean
20795          * @static
20796          */
20797         preventDefault: true,
20798
20799         /**
20800          * Flag to determine if we should stop the propagation of the events
20801          * we generate. This is true by default but you may want to set it to
20802          * false if the html element contains other features that require the
20803          * mouse click.
20804          * @property stopPropagation
20805          * @type boolean
20806          * @static
20807          */
20808         stopPropagation: true,
20809
20810         /**
20811          * Internal flag that is set to true when drag and drop has been
20812          * intialized
20813          * @property initialized
20814          * @private
20815          * @static
20816          */
20817         initalized: false,
20818
20819         /**
20820          * All drag and drop can be disabled.
20821          * @property locked
20822          * @private
20823          * @static
20824          */
20825         locked: false,
20826
20827         /**
20828          * Called the first time an element is registered.
20829          * @method init
20830          * @private
20831          * @static
20832          */
20833         init: function() {
20834             this.initialized = true;
20835         },
20836
20837         /**
20838          * In point mode, drag and drop interaction is defined by the
20839          * location of the cursor during the drag/drop
20840          * @property POINT
20841          * @type int
20842          * @static
20843          */
20844         POINT: 0,
20845
20846         /**
20847          * In intersect mode, drag and drop interactio nis defined by the
20848          * overlap of two or more drag and drop objects.
20849          * @property INTERSECT
20850          * @type int
20851          * @static
20852          */
20853         INTERSECT: 1,
20854
20855         /**
20856          * The current drag and drop mode.  Default: POINT
20857          * @property mode
20858          * @type int
20859          * @static
20860          */
20861         mode: 0,
20862
20863         /**
20864          * Runs method on all drag and drop objects
20865          * @method _execOnAll
20866          * @private
20867          * @static
20868          */
20869         _execOnAll: function(sMethod, args) {
20870             for (var i in this.ids) {
20871                 for (var j in this.ids[i]) {
20872                     var oDD = this.ids[i][j];
20873                     if (! this.isTypeOfDD(oDD)) {
20874                         continue;
20875                     }
20876                     oDD[sMethod].apply(oDD, args);
20877                 }
20878             }
20879         },
20880
20881         /**
20882          * Drag and drop initialization.  Sets up the global event handlers
20883          * @method _onLoad
20884          * @private
20885          * @static
20886          */
20887         _onLoad: function() {
20888
20889             this.init();
20890
20891             if (!Roo.isTouch) {
20892                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20893                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20894             }
20895             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20896             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20897             
20898             Event.on(window,   "unload",    this._onUnload, this, true);
20899             Event.on(window,   "resize",    this._onResize, this, true);
20900             // Event.on(window,   "mouseout",    this._test);
20901
20902         },
20903
20904         /**
20905          * Reset constraints on all drag and drop objs
20906          * @method _onResize
20907          * @private
20908          * @static
20909          */
20910         _onResize: function(e) {
20911             this._execOnAll("resetConstraints", []);
20912         },
20913
20914         /**
20915          * Lock all drag and drop functionality
20916          * @method lock
20917          * @static
20918          */
20919         lock: function() { this.locked = true; },
20920
20921         /**
20922          * Unlock all drag and drop functionality
20923          * @method unlock
20924          * @static
20925          */
20926         unlock: function() { this.locked = false; },
20927
20928         /**
20929          * Is drag and drop locked?
20930          * @method isLocked
20931          * @return {boolean} True if drag and drop is locked, false otherwise.
20932          * @static
20933          */
20934         isLocked: function() { return this.locked; },
20935
20936         /**
20937          * Location cache that is set for all drag drop objects when a drag is
20938          * initiated, cleared when the drag is finished.
20939          * @property locationCache
20940          * @private
20941          * @static
20942          */
20943         locationCache: {},
20944
20945         /**
20946          * Set useCache to false if you want to force object the lookup of each
20947          * drag and drop linked element constantly during a drag.
20948          * @property useCache
20949          * @type boolean
20950          * @static
20951          */
20952         useCache: true,
20953
20954         /**
20955          * The number of pixels that the mouse needs to move after the
20956          * mousedown before the drag is initiated.  Default=3;
20957          * @property clickPixelThresh
20958          * @type int
20959          * @static
20960          */
20961         clickPixelThresh: 3,
20962
20963         /**
20964          * The number of milliseconds after the mousedown event to initiate the
20965          * drag if we don't get a mouseup event. Default=1000
20966          * @property clickTimeThresh
20967          * @type int
20968          * @static
20969          */
20970         clickTimeThresh: 350,
20971
20972         /**
20973          * Flag that indicates that either the drag pixel threshold or the
20974          * mousdown time threshold has been met
20975          * @property dragThreshMet
20976          * @type boolean
20977          * @private
20978          * @static
20979          */
20980         dragThreshMet: false,
20981
20982         /**
20983          * Timeout used for the click time threshold
20984          * @property clickTimeout
20985          * @type Object
20986          * @private
20987          * @static
20988          */
20989         clickTimeout: null,
20990
20991         /**
20992          * The X position of the mousedown event stored for later use when a
20993          * drag threshold is met.
20994          * @property startX
20995          * @type int
20996          * @private
20997          * @static
20998          */
20999         startX: 0,
21000
21001         /**
21002          * The Y position of the mousedown event stored for later use when a
21003          * drag threshold is met.
21004          * @property startY
21005          * @type int
21006          * @private
21007          * @static
21008          */
21009         startY: 0,
21010
21011         /**
21012          * Each DragDrop instance must be registered with the DragDropMgr.
21013          * This is executed in DragDrop.init()
21014          * @method regDragDrop
21015          * @param {DragDrop} oDD the DragDrop object to register
21016          * @param {String} sGroup the name of the group this element belongs to
21017          * @static
21018          */
21019         regDragDrop: function(oDD, sGroup) {
21020             if (!this.initialized) { this.init(); }
21021
21022             if (!this.ids[sGroup]) {
21023                 this.ids[sGroup] = {};
21024             }
21025             this.ids[sGroup][oDD.id] = oDD;
21026         },
21027
21028         /**
21029          * Removes the supplied dd instance from the supplied group. Executed
21030          * by DragDrop.removeFromGroup, so don't call this function directly.
21031          * @method removeDDFromGroup
21032          * @private
21033          * @static
21034          */
21035         removeDDFromGroup: function(oDD, sGroup) {
21036             if (!this.ids[sGroup]) {
21037                 this.ids[sGroup] = {};
21038             }
21039
21040             var obj = this.ids[sGroup];
21041             if (obj && obj[oDD.id]) {
21042                 delete obj[oDD.id];
21043             }
21044         },
21045
21046         /**
21047          * Unregisters a drag and drop item.  This is executed in
21048          * DragDrop.unreg, use that method instead of calling this directly.
21049          * @method _remove
21050          * @private
21051          * @static
21052          */
21053         _remove: function(oDD) {
21054             for (var g in oDD.groups) {
21055                 if (g && this.ids[g][oDD.id]) {
21056                     delete this.ids[g][oDD.id];
21057                 }
21058             }
21059             delete this.handleIds[oDD.id];
21060         },
21061
21062         /**
21063          * Each DragDrop handle element must be registered.  This is done
21064          * automatically when executing DragDrop.setHandleElId()
21065          * @method regHandle
21066          * @param {String} sDDId the DragDrop id this element is a handle for
21067          * @param {String} sHandleId the id of the element that is the drag
21068          * handle
21069          * @static
21070          */
21071         regHandle: function(sDDId, sHandleId) {
21072             if (!this.handleIds[sDDId]) {
21073                 this.handleIds[sDDId] = {};
21074             }
21075             this.handleIds[sDDId][sHandleId] = sHandleId;
21076         },
21077
21078         /**
21079          * Utility function to determine if a given element has been
21080          * registered as a drag drop item.
21081          * @method isDragDrop
21082          * @param {String} id the element id to check
21083          * @return {boolean} true if this element is a DragDrop item,
21084          * false otherwise
21085          * @static
21086          */
21087         isDragDrop: function(id) {
21088             return ( this.getDDById(id) ) ? true : false;
21089         },
21090
21091         /**
21092          * Returns the drag and drop instances that are in all groups the
21093          * passed in instance belongs to.
21094          * @method getRelated
21095          * @param {DragDrop} p_oDD the obj to get related data for
21096          * @param {boolean} bTargetsOnly if true, only return targetable objs
21097          * @return {DragDrop[]} the related instances
21098          * @static
21099          */
21100         getRelated: function(p_oDD, bTargetsOnly) {
21101             var oDDs = [];
21102             for (var i in p_oDD.groups) {
21103                 for (j in this.ids[i]) {
21104                     var dd = this.ids[i][j];
21105                     if (! this.isTypeOfDD(dd)) {
21106                         continue;
21107                     }
21108                     if (!bTargetsOnly || dd.isTarget) {
21109                         oDDs[oDDs.length] = dd;
21110                     }
21111                 }
21112             }
21113
21114             return oDDs;
21115         },
21116
21117         /**
21118          * Returns true if the specified dd target is a legal target for
21119          * the specifice drag obj
21120          * @method isLegalTarget
21121          * @param {DragDrop} the drag obj
21122          * @param {DragDrop} the target
21123          * @return {boolean} true if the target is a legal target for the
21124          * dd obj
21125          * @static
21126          */
21127         isLegalTarget: function (oDD, oTargetDD) {
21128             var targets = this.getRelated(oDD, true);
21129             for (var i=0, len=targets.length;i<len;++i) {
21130                 if (targets[i].id == oTargetDD.id) {
21131                     return true;
21132                 }
21133             }
21134
21135             return false;
21136         },
21137
21138         /**
21139          * My goal is to be able to transparently determine if an object is
21140          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21141          * returns "object", oDD.constructor.toString() always returns
21142          * "DragDrop" and not the name of the subclass.  So for now it just
21143          * evaluates a well-known variable in DragDrop.
21144          * @method isTypeOfDD
21145          * @param {Object} the object to evaluate
21146          * @return {boolean} true if typeof oDD = DragDrop
21147          * @static
21148          */
21149         isTypeOfDD: function (oDD) {
21150             return (oDD && oDD.__ygDragDrop);
21151         },
21152
21153         /**
21154          * Utility function to determine if a given element has been
21155          * registered as a drag drop handle for the given Drag Drop object.
21156          * @method isHandle
21157          * @param {String} id the element id to check
21158          * @return {boolean} true if this element is a DragDrop handle, false
21159          * otherwise
21160          * @static
21161          */
21162         isHandle: function(sDDId, sHandleId) {
21163             return ( this.handleIds[sDDId] &&
21164                             this.handleIds[sDDId][sHandleId] );
21165         },
21166
21167         /**
21168          * Returns the DragDrop instance for a given id
21169          * @method getDDById
21170          * @param {String} id the id of the DragDrop object
21171          * @return {DragDrop} the drag drop object, null if it is not found
21172          * @static
21173          */
21174         getDDById: function(id) {
21175             for (var i in this.ids) {
21176                 if (this.ids[i][id]) {
21177                     return this.ids[i][id];
21178                 }
21179             }
21180             return null;
21181         },
21182
21183         /**
21184          * Fired after a registered DragDrop object gets the mousedown event.
21185          * Sets up the events required to track the object being dragged
21186          * @method handleMouseDown
21187          * @param {Event} e the event
21188          * @param oDD the DragDrop object being dragged
21189          * @private
21190          * @static
21191          */
21192         handleMouseDown: function(e, oDD) {
21193             if(Roo.QuickTips){
21194                 Roo.QuickTips.disable();
21195             }
21196             this.currentTarget = e.getTarget();
21197
21198             this.dragCurrent = oDD;
21199
21200             var el = oDD.getEl();
21201
21202             // track start position
21203             this.startX = e.getPageX();
21204             this.startY = e.getPageY();
21205
21206             this.deltaX = this.startX - el.offsetLeft;
21207             this.deltaY = this.startY - el.offsetTop;
21208
21209             this.dragThreshMet = false;
21210
21211             this.clickTimeout = setTimeout(
21212                     function() {
21213                         var DDM = Roo.dd.DDM;
21214                         DDM.startDrag(DDM.startX, DDM.startY);
21215                     },
21216                     this.clickTimeThresh );
21217         },
21218
21219         /**
21220          * Fired when either the drag pixel threshol or the mousedown hold
21221          * time threshold has been met.
21222          * @method startDrag
21223          * @param x {int} the X position of the original mousedown
21224          * @param y {int} the Y position of the original mousedown
21225          * @static
21226          */
21227         startDrag: function(x, y) {
21228             clearTimeout(this.clickTimeout);
21229             if (this.dragCurrent) {
21230                 this.dragCurrent.b4StartDrag(x, y);
21231                 this.dragCurrent.startDrag(x, y);
21232             }
21233             this.dragThreshMet = true;
21234         },
21235
21236         /**
21237          * Internal function to handle the mouseup event.  Will be invoked
21238          * from the context of the document.
21239          * @method handleMouseUp
21240          * @param {Event} e the event
21241          * @private
21242          * @static
21243          */
21244         handleMouseUp: function(e) {
21245
21246             if(Roo.QuickTips){
21247                 Roo.QuickTips.enable();
21248             }
21249             if (! this.dragCurrent) {
21250                 return;
21251             }
21252
21253             clearTimeout(this.clickTimeout);
21254
21255             if (this.dragThreshMet) {
21256                 this.fireEvents(e, true);
21257             } else {
21258             }
21259
21260             this.stopDrag(e);
21261
21262             this.stopEvent(e);
21263         },
21264
21265         /**
21266          * Utility to stop event propagation and event default, if these
21267          * features are turned on.
21268          * @method stopEvent
21269          * @param {Event} e the event as returned by this.getEvent()
21270          * @static
21271          */
21272         stopEvent: function(e){
21273             if(this.stopPropagation) {
21274                 e.stopPropagation();
21275             }
21276
21277             if (this.preventDefault) {
21278                 e.preventDefault();
21279             }
21280         },
21281
21282         /**
21283          * Internal function to clean up event handlers after the drag
21284          * operation is complete
21285          * @method stopDrag
21286          * @param {Event} e the event
21287          * @private
21288          * @static
21289          */
21290         stopDrag: function(e) {
21291             // Fire the drag end event for the item that was dragged
21292             if (this.dragCurrent) {
21293                 if (this.dragThreshMet) {
21294                     this.dragCurrent.b4EndDrag(e);
21295                     this.dragCurrent.endDrag(e);
21296                 }
21297
21298                 this.dragCurrent.onMouseUp(e);
21299             }
21300
21301             this.dragCurrent = null;
21302             this.dragOvers = {};
21303         },
21304
21305         /**
21306          * Internal function to handle the mousemove event.  Will be invoked
21307          * from the context of the html element.
21308          *
21309          * @TODO figure out what we can do about mouse events lost when the
21310          * user drags objects beyond the window boundary.  Currently we can
21311          * detect this in internet explorer by verifying that the mouse is
21312          * down during the mousemove event.  Firefox doesn't give us the
21313          * button state on the mousemove event.
21314          * @method handleMouseMove
21315          * @param {Event} e the event
21316          * @private
21317          * @static
21318          */
21319         handleMouseMove: function(e) {
21320             if (! this.dragCurrent) {
21321                 return true;
21322             }
21323
21324             // var button = e.which || e.button;
21325
21326             // check for IE mouseup outside of page boundary
21327             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21328                 this.stopEvent(e);
21329                 return this.handleMouseUp(e);
21330             }
21331
21332             if (!this.dragThreshMet) {
21333                 var diffX = Math.abs(this.startX - e.getPageX());
21334                 var diffY = Math.abs(this.startY - e.getPageY());
21335                 if (diffX > this.clickPixelThresh ||
21336                             diffY > this.clickPixelThresh) {
21337                     this.startDrag(this.startX, this.startY);
21338                 }
21339             }
21340
21341             if (this.dragThreshMet) {
21342                 this.dragCurrent.b4Drag(e);
21343                 this.dragCurrent.onDrag(e);
21344                 if(!this.dragCurrent.moveOnly){
21345                     this.fireEvents(e, false);
21346                 }
21347             }
21348
21349             this.stopEvent(e);
21350
21351             return true;
21352         },
21353
21354         /**
21355          * Iterates over all of the DragDrop elements to find ones we are
21356          * hovering over or dropping on
21357          * @method fireEvents
21358          * @param {Event} e the event
21359          * @param {boolean} isDrop is this a drop op or a mouseover op?
21360          * @private
21361          * @static
21362          */
21363         fireEvents: function(e, isDrop) {
21364             var dc = this.dragCurrent;
21365
21366             // If the user did the mouse up outside of the window, we could
21367             // get here even though we have ended the drag.
21368             if (!dc || dc.isLocked()) {
21369                 return;
21370             }
21371
21372             var pt = e.getPoint();
21373
21374             // cache the previous dragOver array
21375             var oldOvers = [];
21376
21377             var outEvts   = [];
21378             var overEvts  = [];
21379             var dropEvts  = [];
21380             var enterEvts = [];
21381
21382             // Check to see if the object(s) we were hovering over is no longer
21383             // being hovered over so we can fire the onDragOut event
21384             for (var i in this.dragOvers) {
21385
21386                 var ddo = this.dragOvers[i];
21387
21388                 if (! this.isTypeOfDD(ddo)) {
21389                     continue;
21390                 }
21391
21392                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21393                     outEvts.push( ddo );
21394                 }
21395
21396                 oldOvers[i] = true;
21397                 delete this.dragOvers[i];
21398             }
21399
21400             for (var sGroup in dc.groups) {
21401
21402                 if ("string" != typeof sGroup) {
21403                     continue;
21404                 }
21405
21406                 for (i in this.ids[sGroup]) {
21407                     var oDD = this.ids[sGroup][i];
21408                     if (! this.isTypeOfDD(oDD)) {
21409                         continue;
21410                     }
21411
21412                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21413                         if (this.isOverTarget(pt, oDD, this.mode)) {
21414                             // look for drop interactions
21415                             if (isDrop) {
21416                                 dropEvts.push( oDD );
21417                             // look for drag enter and drag over interactions
21418                             } else {
21419
21420                                 // initial drag over: dragEnter fires
21421                                 if (!oldOvers[oDD.id]) {
21422                                     enterEvts.push( oDD );
21423                                 // subsequent drag overs: dragOver fires
21424                                 } else {
21425                                     overEvts.push( oDD );
21426                                 }
21427
21428                                 this.dragOvers[oDD.id] = oDD;
21429                             }
21430                         }
21431                     }
21432                 }
21433             }
21434
21435             if (this.mode) {
21436                 if (outEvts.length) {
21437                     dc.b4DragOut(e, outEvts);
21438                     dc.onDragOut(e, outEvts);
21439                 }
21440
21441                 if (enterEvts.length) {
21442                     dc.onDragEnter(e, enterEvts);
21443                 }
21444
21445                 if (overEvts.length) {
21446                     dc.b4DragOver(e, overEvts);
21447                     dc.onDragOver(e, overEvts);
21448                 }
21449
21450                 if (dropEvts.length) {
21451                     dc.b4DragDrop(e, dropEvts);
21452                     dc.onDragDrop(e, dropEvts);
21453                 }
21454
21455             } else {
21456                 // fire dragout events
21457                 var len = 0;
21458                 for (i=0, len=outEvts.length; i<len; ++i) {
21459                     dc.b4DragOut(e, outEvts[i].id);
21460                     dc.onDragOut(e, outEvts[i].id);
21461                 }
21462
21463                 // fire enter events
21464                 for (i=0,len=enterEvts.length; i<len; ++i) {
21465                     // dc.b4DragEnter(e, oDD.id);
21466                     dc.onDragEnter(e, enterEvts[i].id);
21467                 }
21468
21469                 // fire over events
21470                 for (i=0,len=overEvts.length; i<len; ++i) {
21471                     dc.b4DragOver(e, overEvts[i].id);
21472                     dc.onDragOver(e, overEvts[i].id);
21473                 }
21474
21475                 // fire drop events
21476                 for (i=0, len=dropEvts.length; i<len; ++i) {
21477                     dc.b4DragDrop(e, dropEvts[i].id);
21478                     dc.onDragDrop(e, dropEvts[i].id);
21479                 }
21480
21481             }
21482
21483             // notify about a drop that did not find a target
21484             if (isDrop && !dropEvts.length) {
21485                 dc.onInvalidDrop(e);
21486             }
21487
21488         },
21489
21490         /**
21491          * Helper function for getting the best match from the list of drag
21492          * and drop objects returned by the drag and drop events when we are
21493          * in INTERSECT mode.  It returns either the first object that the
21494          * cursor is over, or the object that has the greatest overlap with
21495          * the dragged element.
21496          * @method getBestMatch
21497          * @param  {DragDrop[]} dds The array of drag and drop objects
21498          * targeted
21499          * @return {DragDrop}       The best single match
21500          * @static
21501          */
21502         getBestMatch: function(dds) {
21503             var winner = null;
21504             // Return null if the input is not what we expect
21505             //if (!dds || !dds.length || dds.length == 0) {
21506                // winner = null;
21507             // If there is only one item, it wins
21508             //} else if (dds.length == 1) {
21509
21510             var len = dds.length;
21511
21512             if (len == 1) {
21513                 winner = dds[0];
21514             } else {
21515                 // Loop through the targeted items
21516                 for (var i=0; i<len; ++i) {
21517                     var dd = dds[i];
21518                     // If the cursor is over the object, it wins.  If the
21519                     // cursor is over multiple matches, the first one we come
21520                     // to wins.
21521                     if (dd.cursorIsOver) {
21522                         winner = dd;
21523                         break;
21524                     // Otherwise the object with the most overlap wins
21525                     } else {
21526                         if (!winner ||
21527                             winner.overlap.getArea() < dd.overlap.getArea()) {
21528                             winner = dd;
21529                         }
21530                     }
21531                 }
21532             }
21533
21534             return winner;
21535         },
21536
21537         /**
21538          * Refreshes the cache of the top-left and bottom-right points of the
21539          * drag and drop objects in the specified group(s).  This is in the
21540          * format that is stored in the drag and drop instance, so typical
21541          * usage is:
21542          * <code>
21543          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21544          * </code>
21545          * Alternatively:
21546          * <code>
21547          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21548          * </code>
21549          * @TODO this really should be an indexed array.  Alternatively this
21550          * method could accept both.
21551          * @method refreshCache
21552          * @param {Object} groups an associative array of groups to refresh
21553          * @static
21554          */
21555         refreshCache: function(groups) {
21556             for (var sGroup in groups) {
21557                 if ("string" != typeof sGroup) {
21558                     continue;
21559                 }
21560                 for (var i in this.ids[sGroup]) {
21561                     var oDD = this.ids[sGroup][i];
21562
21563                     if (this.isTypeOfDD(oDD)) {
21564                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21565                         var loc = this.getLocation(oDD);
21566                         if (loc) {
21567                             this.locationCache[oDD.id] = loc;
21568                         } else {
21569                             delete this.locationCache[oDD.id];
21570                             // this will unregister the drag and drop object if
21571                             // the element is not in a usable state
21572                             // oDD.unreg();
21573                         }
21574                     }
21575                 }
21576             }
21577         },
21578
21579         /**
21580          * This checks to make sure an element exists and is in the DOM.  The
21581          * main purpose is to handle cases where innerHTML is used to remove
21582          * drag and drop objects from the DOM.  IE provides an 'unspecified
21583          * error' when trying to access the offsetParent of such an element
21584          * @method verifyEl
21585          * @param {HTMLElement} el the element to check
21586          * @return {boolean} true if the element looks usable
21587          * @static
21588          */
21589         verifyEl: function(el) {
21590             if (el) {
21591                 var parent;
21592                 if(Roo.isIE){
21593                     try{
21594                         parent = el.offsetParent;
21595                     }catch(e){}
21596                 }else{
21597                     parent = el.offsetParent;
21598                 }
21599                 if (parent) {
21600                     return true;
21601                 }
21602             }
21603
21604             return false;
21605         },
21606
21607         /**
21608          * Returns a Region object containing the drag and drop element's position
21609          * and size, including the padding configured for it
21610          * @method getLocation
21611          * @param {DragDrop} oDD the drag and drop object to get the
21612          *                       location for
21613          * @return {Roo.lib.Region} a Region object representing the total area
21614          *                             the element occupies, including any padding
21615          *                             the instance is configured for.
21616          * @static
21617          */
21618         getLocation: function(oDD) {
21619             if (! this.isTypeOfDD(oDD)) {
21620                 return null;
21621             }
21622
21623             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21624
21625             try {
21626                 pos= Roo.lib.Dom.getXY(el);
21627             } catch (e) { }
21628
21629             if (!pos) {
21630                 return null;
21631             }
21632
21633             x1 = pos[0];
21634             x2 = x1 + el.offsetWidth;
21635             y1 = pos[1];
21636             y2 = y1 + el.offsetHeight;
21637
21638             t = y1 - oDD.padding[0];
21639             r = x2 + oDD.padding[1];
21640             b = y2 + oDD.padding[2];
21641             l = x1 - oDD.padding[3];
21642
21643             return new Roo.lib.Region( t, r, b, l );
21644         },
21645
21646         /**
21647          * Checks the cursor location to see if it over the target
21648          * @method isOverTarget
21649          * @param {Roo.lib.Point} pt The point to evaluate
21650          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21651          * @return {boolean} true if the mouse is over the target
21652          * @private
21653          * @static
21654          */
21655         isOverTarget: function(pt, oTarget, intersect) {
21656             // use cache if available
21657             var loc = this.locationCache[oTarget.id];
21658             if (!loc || !this.useCache) {
21659                 loc = this.getLocation(oTarget);
21660                 this.locationCache[oTarget.id] = loc;
21661
21662             }
21663
21664             if (!loc) {
21665                 return false;
21666             }
21667
21668             oTarget.cursorIsOver = loc.contains( pt );
21669
21670             // DragDrop is using this as a sanity check for the initial mousedown
21671             // in this case we are done.  In POINT mode, if the drag obj has no
21672             // contraints, we are also done. Otherwise we need to evaluate the
21673             // location of the target as related to the actual location of the
21674             // dragged element.
21675             var dc = this.dragCurrent;
21676             if (!dc || !dc.getTargetCoord ||
21677                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21678                 return oTarget.cursorIsOver;
21679             }
21680
21681             oTarget.overlap = null;
21682
21683             // Get the current location of the drag element, this is the
21684             // location of the mouse event less the delta that represents
21685             // where the original mousedown happened on the element.  We
21686             // need to consider constraints and ticks as well.
21687             var pos = dc.getTargetCoord(pt.x, pt.y);
21688
21689             var el = dc.getDragEl();
21690             var curRegion = new Roo.lib.Region( pos.y,
21691                                                    pos.x + el.offsetWidth,
21692                                                    pos.y + el.offsetHeight,
21693                                                    pos.x );
21694
21695             var overlap = curRegion.intersect(loc);
21696
21697             if (overlap) {
21698                 oTarget.overlap = overlap;
21699                 return (intersect) ? true : oTarget.cursorIsOver;
21700             } else {
21701                 return false;
21702             }
21703         },
21704
21705         /**
21706          * unload event handler
21707          * @method _onUnload
21708          * @private
21709          * @static
21710          */
21711         _onUnload: function(e, me) {
21712             Roo.dd.DragDropMgr.unregAll();
21713         },
21714
21715         /**
21716          * Cleans up the drag and drop events and objects.
21717          * @method unregAll
21718          * @private
21719          * @static
21720          */
21721         unregAll: function() {
21722
21723             if (this.dragCurrent) {
21724                 this.stopDrag();
21725                 this.dragCurrent = null;
21726             }
21727
21728             this._execOnAll("unreg", []);
21729
21730             for (i in this.elementCache) {
21731                 delete this.elementCache[i];
21732             }
21733
21734             this.elementCache = {};
21735             this.ids = {};
21736         },
21737
21738         /**
21739          * A cache of DOM elements
21740          * @property elementCache
21741          * @private
21742          * @static
21743          */
21744         elementCache: {},
21745
21746         /**
21747          * Get the wrapper for the DOM element specified
21748          * @method getElWrapper
21749          * @param {String} id the id of the element to get
21750          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21751          * @private
21752          * @deprecated This wrapper isn't that useful
21753          * @static
21754          */
21755         getElWrapper: function(id) {
21756             var oWrapper = this.elementCache[id];
21757             if (!oWrapper || !oWrapper.el) {
21758                 oWrapper = this.elementCache[id] =
21759                     new this.ElementWrapper(Roo.getDom(id));
21760             }
21761             return oWrapper;
21762         },
21763
21764         /**
21765          * Returns the actual DOM element
21766          * @method getElement
21767          * @param {String} id the id of the elment to get
21768          * @return {Object} The element
21769          * @deprecated use Roo.getDom instead
21770          * @static
21771          */
21772         getElement: function(id) {
21773             return Roo.getDom(id);
21774         },
21775
21776         /**
21777          * Returns the style property for the DOM element (i.e.,
21778          * document.getElById(id).style)
21779          * @method getCss
21780          * @param {String} id the id of the elment to get
21781          * @return {Object} The style property of the element
21782          * @deprecated use Roo.getDom instead
21783          * @static
21784          */
21785         getCss: function(id) {
21786             var el = Roo.getDom(id);
21787             return (el) ? el.style : null;
21788         },
21789
21790         /**
21791          * Inner class for cached elements
21792          * @class DragDropMgr.ElementWrapper
21793          * @for DragDropMgr
21794          * @private
21795          * @deprecated
21796          */
21797         ElementWrapper: function(el) {
21798                 /**
21799                  * The element
21800                  * @property el
21801                  */
21802                 this.el = el || null;
21803                 /**
21804                  * The element id
21805                  * @property id
21806                  */
21807                 this.id = this.el && el.id;
21808                 /**
21809                  * A reference to the style property
21810                  * @property css
21811                  */
21812                 this.css = this.el && el.style;
21813             },
21814
21815         /**
21816          * Returns the X position of an html element
21817          * @method getPosX
21818          * @param el the element for which to get the position
21819          * @return {int} the X coordinate
21820          * @for DragDropMgr
21821          * @deprecated use Roo.lib.Dom.getX instead
21822          * @static
21823          */
21824         getPosX: function(el) {
21825             return Roo.lib.Dom.getX(el);
21826         },
21827
21828         /**
21829          * Returns the Y position of an html element
21830          * @method getPosY
21831          * @param el the element for which to get the position
21832          * @return {int} the Y coordinate
21833          * @deprecated use Roo.lib.Dom.getY instead
21834          * @static
21835          */
21836         getPosY: function(el) {
21837             return Roo.lib.Dom.getY(el);
21838         },
21839
21840         /**
21841          * Swap two nodes.  In IE, we use the native method, for others we
21842          * emulate the IE behavior
21843          * @method swapNode
21844          * @param n1 the first node to swap
21845          * @param n2 the other node to swap
21846          * @static
21847          */
21848         swapNode: function(n1, n2) {
21849             if (n1.swapNode) {
21850                 n1.swapNode(n2);
21851             } else {
21852                 var p = n2.parentNode;
21853                 var s = n2.nextSibling;
21854
21855                 if (s == n1) {
21856                     p.insertBefore(n1, n2);
21857                 } else if (n2 == n1.nextSibling) {
21858                     p.insertBefore(n2, n1);
21859                 } else {
21860                     n1.parentNode.replaceChild(n2, n1);
21861                     p.insertBefore(n1, s);
21862                 }
21863             }
21864         },
21865
21866         /**
21867          * Returns the current scroll position
21868          * @method getScroll
21869          * @private
21870          * @static
21871          */
21872         getScroll: function () {
21873             var t, l, dde=document.documentElement, db=document.body;
21874             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21875                 t = dde.scrollTop;
21876                 l = dde.scrollLeft;
21877             } else if (db) {
21878                 t = db.scrollTop;
21879                 l = db.scrollLeft;
21880             } else {
21881
21882             }
21883             return { top: t, left: l };
21884         },
21885
21886         /**
21887          * Returns the specified element style property
21888          * @method getStyle
21889          * @param {HTMLElement} el          the element
21890          * @param {string}      styleProp   the style property
21891          * @return {string} The value of the style property
21892          * @deprecated use Roo.lib.Dom.getStyle
21893          * @static
21894          */
21895         getStyle: function(el, styleProp) {
21896             return Roo.fly(el).getStyle(styleProp);
21897         },
21898
21899         /**
21900          * Gets the scrollTop
21901          * @method getScrollTop
21902          * @return {int} the document's scrollTop
21903          * @static
21904          */
21905         getScrollTop: function () { return this.getScroll().top; },
21906
21907         /**
21908          * Gets the scrollLeft
21909          * @method getScrollLeft
21910          * @return {int} the document's scrollTop
21911          * @static
21912          */
21913         getScrollLeft: function () { return this.getScroll().left; },
21914
21915         /**
21916          * Sets the x/y position of an element to the location of the
21917          * target element.
21918          * @method moveToEl
21919          * @param {HTMLElement} moveEl      The element to move
21920          * @param {HTMLElement} targetEl    The position reference element
21921          * @static
21922          */
21923         moveToEl: function (moveEl, targetEl) {
21924             var aCoord = Roo.lib.Dom.getXY(targetEl);
21925             Roo.lib.Dom.setXY(moveEl, aCoord);
21926         },
21927
21928         /**
21929          * Numeric array sort function
21930          * @method numericSort
21931          * @static
21932          */
21933         numericSort: function(a, b) { return (a - b); },
21934
21935         /**
21936          * Internal counter
21937          * @property _timeoutCount
21938          * @private
21939          * @static
21940          */
21941         _timeoutCount: 0,
21942
21943         /**
21944          * Trying to make the load order less important.  Without this we get
21945          * an error if this file is loaded before the Event Utility.
21946          * @method _addListeners
21947          * @private
21948          * @static
21949          */
21950         _addListeners: function() {
21951             var DDM = Roo.dd.DDM;
21952             if ( Roo.lib.Event && document ) {
21953                 DDM._onLoad();
21954             } else {
21955                 if (DDM._timeoutCount > 2000) {
21956                 } else {
21957                     setTimeout(DDM._addListeners, 10);
21958                     if (document && document.body) {
21959                         DDM._timeoutCount += 1;
21960                     }
21961                 }
21962             }
21963         },
21964
21965         /**
21966          * Recursively searches the immediate parent and all child nodes for
21967          * the handle element in order to determine wheter or not it was
21968          * clicked.
21969          * @method handleWasClicked
21970          * @param node the html element to inspect
21971          * @static
21972          */
21973         handleWasClicked: function(node, id) {
21974             if (this.isHandle(id, node.id)) {
21975                 return true;
21976             } else {
21977                 // check to see if this is a text node child of the one we want
21978                 var p = node.parentNode;
21979
21980                 while (p) {
21981                     if (this.isHandle(id, p.id)) {
21982                         return true;
21983                     } else {
21984                         p = p.parentNode;
21985                     }
21986                 }
21987             }
21988
21989             return false;
21990         }
21991
21992     };
21993
21994 }();
21995
21996 // shorter alias, save a few bytes
21997 Roo.dd.DDM = Roo.dd.DragDropMgr;
21998 Roo.dd.DDM._addListeners();
21999
22000 }/*
22001  * Based on:
22002  * Ext JS Library 1.1.1
22003  * Copyright(c) 2006-2007, Ext JS, LLC.
22004  *
22005  * Originally Released Under LGPL - original licence link has changed is not relivant.
22006  *
22007  * Fork - LGPL
22008  * <script type="text/javascript">
22009  */
22010
22011 /**
22012  * @class Roo.dd.DD
22013  * A DragDrop implementation where the linked element follows the
22014  * mouse cursor during a drag.
22015  * @extends Roo.dd.DragDrop
22016  * @constructor
22017  * @param {String} id the id of the linked element
22018  * @param {String} sGroup the group of related DragDrop items
22019  * @param {object} config an object containing configurable attributes
22020  *                Valid properties for DD:
22021  *                    scroll
22022  */
22023 Roo.dd.DD = function(id, sGroup, config) {
22024     if (id) {
22025         this.init(id, sGroup, config);
22026     }
22027 };
22028
22029 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22030
22031     /**
22032      * When set to true, the utility automatically tries to scroll the browser
22033      * window wehn a drag and drop element is dragged near the viewport boundary.
22034      * Defaults to true.
22035      * @property scroll
22036      * @type boolean
22037      */
22038     scroll: true,
22039
22040     /**
22041      * Sets the pointer offset to the distance between the linked element's top
22042      * left corner and the location the element was clicked
22043      * @method autoOffset
22044      * @param {int} iPageX the X coordinate of the click
22045      * @param {int} iPageY the Y coordinate of the click
22046      */
22047     autoOffset: function(iPageX, iPageY) {
22048         var x = iPageX - this.startPageX;
22049         var y = iPageY - this.startPageY;
22050         this.setDelta(x, y);
22051     },
22052
22053     /**
22054      * Sets the pointer offset.  You can call this directly to force the
22055      * offset to be in a particular location (e.g., pass in 0,0 to set it
22056      * to the center of the object)
22057      * @method setDelta
22058      * @param {int} iDeltaX the distance from the left
22059      * @param {int} iDeltaY the distance from the top
22060      */
22061     setDelta: function(iDeltaX, iDeltaY) {
22062         this.deltaX = iDeltaX;
22063         this.deltaY = iDeltaY;
22064     },
22065
22066     /**
22067      * Sets the drag element to the location of the mousedown or click event,
22068      * maintaining the cursor location relative to the location on the element
22069      * that was clicked.  Override this if you want to place the element in a
22070      * location other than where the cursor is.
22071      * @method setDragElPos
22072      * @param {int} iPageX the X coordinate of the mousedown or drag event
22073      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22074      */
22075     setDragElPos: function(iPageX, iPageY) {
22076         // the first time we do this, we are going to check to make sure
22077         // the element has css positioning
22078
22079         var el = this.getDragEl();
22080         this.alignElWithMouse(el, iPageX, iPageY);
22081     },
22082
22083     /**
22084      * Sets the element to the location of the mousedown or click event,
22085      * maintaining the cursor location relative to the location on the element
22086      * that was clicked.  Override this if you want to place the element in a
22087      * location other than where the cursor is.
22088      * @method alignElWithMouse
22089      * @param {HTMLElement} el the element to move
22090      * @param {int} iPageX the X coordinate of the mousedown or drag event
22091      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22092      */
22093     alignElWithMouse: function(el, iPageX, iPageY) {
22094         var oCoord = this.getTargetCoord(iPageX, iPageY);
22095         var fly = el.dom ? el : Roo.fly(el);
22096         if (!this.deltaSetXY) {
22097             var aCoord = [oCoord.x, oCoord.y];
22098             fly.setXY(aCoord);
22099             var newLeft = fly.getLeft(true);
22100             var newTop  = fly.getTop(true);
22101             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22102         } else {
22103             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22104         }
22105
22106         this.cachePosition(oCoord.x, oCoord.y);
22107         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22108         return oCoord;
22109     },
22110
22111     /**
22112      * Saves the most recent position so that we can reset the constraints and
22113      * tick marks on-demand.  We need to know this so that we can calculate the
22114      * number of pixels the element is offset from its original position.
22115      * @method cachePosition
22116      * @param iPageX the current x position (optional, this just makes it so we
22117      * don't have to look it up again)
22118      * @param iPageY the current y position (optional, this just makes it so we
22119      * don't have to look it up again)
22120      */
22121     cachePosition: function(iPageX, iPageY) {
22122         if (iPageX) {
22123             this.lastPageX = iPageX;
22124             this.lastPageY = iPageY;
22125         } else {
22126             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22127             this.lastPageX = aCoord[0];
22128             this.lastPageY = aCoord[1];
22129         }
22130     },
22131
22132     /**
22133      * Auto-scroll the window if the dragged object has been moved beyond the
22134      * visible window boundary.
22135      * @method autoScroll
22136      * @param {int} x the drag element's x position
22137      * @param {int} y the drag element's y position
22138      * @param {int} h the height of the drag element
22139      * @param {int} w the width of the drag element
22140      * @private
22141      */
22142     autoScroll: function(x, y, h, w) {
22143
22144         if (this.scroll) {
22145             // The client height
22146             var clientH = Roo.lib.Dom.getViewWidth();
22147
22148             // The client width
22149             var clientW = Roo.lib.Dom.getViewHeight();
22150
22151             // The amt scrolled down
22152             var st = this.DDM.getScrollTop();
22153
22154             // The amt scrolled right
22155             var sl = this.DDM.getScrollLeft();
22156
22157             // Location of the bottom of the element
22158             var bot = h + y;
22159
22160             // Location of the right of the element
22161             var right = w + x;
22162
22163             // The distance from the cursor to the bottom of the visible area,
22164             // adjusted so that we don't scroll if the cursor is beyond the
22165             // element drag constraints
22166             var toBot = (clientH + st - y - this.deltaY);
22167
22168             // The distance from the cursor to the right of the visible area
22169             var toRight = (clientW + sl - x - this.deltaX);
22170
22171
22172             // How close to the edge the cursor must be before we scroll
22173             // var thresh = (document.all) ? 100 : 40;
22174             var thresh = 40;
22175
22176             // How many pixels to scroll per autoscroll op.  This helps to reduce
22177             // clunky scrolling. IE is more sensitive about this ... it needs this
22178             // value to be higher.
22179             var scrAmt = (document.all) ? 80 : 30;
22180
22181             // Scroll down if we are near the bottom of the visible page and the
22182             // obj extends below the crease
22183             if ( bot > clientH && toBot < thresh ) {
22184                 window.scrollTo(sl, st + scrAmt);
22185             }
22186
22187             // Scroll up if the window is scrolled down and the top of the object
22188             // goes above the top border
22189             if ( y < st && st > 0 && y - st < thresh ) {
22190                 window.scrollTo(sl, st - scrAmt);
22191             }
22192
22193             // Scroll right if the obj is beyond the right border and the cursor is
22194             // near the border.
22195             if ( right > clientW && toRight < thresh ) {
22196                 window.scrollTo(sl + scrAmt, st);
22197             }
22198
22199             // Scroll left if the window has been scrolled to the right and the obj
22200             // extends past the left border
22201             if ( x < sl && sl > 0 && x - sl < thresh ) {
22202                 window.scrollTo(sl - scrAmt, st);
22203             }
22204         }
22205     },
22206
22207     /**
22208      * Finds the location the element should be placed if we want to move
22209      * it to where the mouse location less the click offset would place us.
22210      * @method getTargetCoord
22211      * @param {int} iPageX the X coordinate of the click
22212      * @param {int} iPageY the Y coordinate of the click
22213      * @return an object that contains the coordinates (Object.x and Object.y)
22214      * @private
22215      */
22216     getTargetCoord: function(iPageX, iPageY) {
22217
22218
22219         var x = iPageX - this.deltaX;
22220         var y = iPageY - this.deltaY;
22221
22222         if (this.constrainX) {
22223             if (x < this.minX) { x = this.minX; }
22224             if (x > this.maxX) { x = this.maxX; }
22225         }
22226
22227         if (this.constrainY) {
22228             if (y < this.minY) { y = this.minY; }
22229             if (y > this.maxY) { y = this.maxY; }
22230         }
22231
22232         x = this.getTick(x, this.xTicks);
22233         y = this.getTick(y, this.yTicks);
22234
22235
22236         return {x:x, y:y};
22237     },
22238
22239     /*
22240      * Sets up config options specific to this class. Overrides
22241      * Roo.dd.DragDrop, but all versions of this method through the
22242      * inheritance chain are called
22243      */
22244     applyConfig: function() {
22245         Roo.dd.DD.superclass.applyConfig.call(this);
22246         this.scroll = (this.config.scroll !== false);
22247     },
22248
22249     /*
22250      * Event that fires prior to the onMouseDown event.  Overrides
22251      * Roo.dd.DragDrop.
22252      */
22253     b4MouseDown: function(e) {
22254         // this.resetConstraints();
22255         this.autoOffset(e.getPageX(),
22256                             e.getPageY());
22257     },
22258
22259     /*
22260      * Event that fires prior to the onDrag event.  Overrides
22261      * Roo.dd.DragDrop.
22262      */
22263     b4Drag: function(e) {
22264         this.setDragElPos(e.getPageX(),
22265                             e.getPageY());
22266     },
22267
22268     toString: function() {
22269         return ("DD " + this.id);
22270     }
22271
22272     //////////////////////////////////////////////////////////////////////////
22273     // Debugging ygDragDrop events that can be overridden
22274     //////////////////////////////////////////////////////////////////////////
22275     /*
22276     startDrag: function(x, y) {
22277     },
22278
22279     onDrag: function(e) {
22280     },
22281
22282     onDragEnter: function(e, id) {
22283     },
22284
22285     onDragOver: function(e, id) {
22286     },
22287
22288     onDragOut: function(e, id) {
22289     },
22290
22291     onDragDrop: function(e, id) {
22292     },
22293
22294     endDrag: function(e) {
22295     }
22296
22297     */
22298
22299 });/*
22300  * Based on:
22301  * Ext JS Library 1.1.1
22302  * Copyright(c) 2006-2007, Ext JS, LLC.
22303  *
22304  * Originally Released Under LGPL - original licence link has changed is not relivant.
22305  *
22306  * Fork - LGPL
22307  * <script type="text/javascript">
22308  */
22309
22310 /**
22311  * @class Roo.dd.DDProxy
22312  * A DragDrop implementation that inserts an empty, bordered div into
22313  * the document that follows the cursor during drag operations.  At the time of
22314  * the click, the frame div is resized to the dimensions of the linked html
22315  * element, and moved to the exact location of the linked element.
22316  *
22317  * References to the "frame" element refer to the single proxy element that
22318  * was created to be dragged in place of all DDProxy elements on the
22319  * page.
22320  *
22321  * @extends Roo.dd.DD
22322  * @constructor
22323  * @param {String} id the id of the linked html element
22324  * @param {String} sGroup the group of related DragDrop objects
22325  * @param {object} config an object containing configurable attributes
22326  *                Valid properties for DDProxy in addition to those in DragDrop:
22327  *                   resizeFrame, centerFrame, dragElId
22328  */
22329 Roo.dd.DDProxy = function(id, sGroup, config) {
22330     if (id) {
22331         this.init(id, sGroup, config);
22332         this.initFrame();
22333     }
22334 };
22335
22336 /**
22337  * The default drag frame div id
22338  * @property Roo.dd.DDProxy.dragElId
22339  * @type String
22340  * @static
22341  */
22342 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22343
22344 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22345
22346     /**
22347      * By default we resize the drag frame to be the same size as the element
22348      * we want to drag (this is to get the frame effect).  We can turn it off
22349      * if we want a different behavior.
22350      * @property resizeFrame
22351      * @type boolean
22352      */
22353     resizeFrame: true,
22354
22355     /**
22356      * By default the frame is positioned exactly where the drag element is, so
22357      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22358      * you do not have constraints on the obj is to have the drag frame centered
22359      * around the cursor.  Set centerFrame to true for this effect.
22360      * @property centerFrame
22361      * @type boolean
22362      */
22363     centerFrame: false,
22364
22365     /**
22366      * Creates the proxy element if it does not yet exist
22367      * @method createFrame
22368      */
22369     createFrame: function() {
22370         var self = this;
22371         var body = document.body;
22372
22373         if (!body || !body.firstChild) {
22374             setTimeout( function() { self.createFrame(); }, 50 );
22375             return;
22376         }
22377
22378         var div = this.getDragEl();
22379
22380         if (!div) {
22381             div    = document.createElement("div");
22382             div.id = this.dragElId;
22383             var s  = div.style;
22384
22385             s.position   = "absolute";
22386             s.visibility = "hidden";
22387             s.cursor     = "move";
22388             s.border     = "2px solid #aaa";
22389             s.zIndex     = 999;
22390
22391             // appendChild can blow up IE if invoked prior to the window load event
22392             // while rendering a table.  It is possible there are other scenarios
22393             // that would cause this to happen as well.
22394             body.insertBefore(div, body.firstChild);
22395         }
22396     },
22397
22398     /**
22399      * Initialization for the drag frame element.  Must be called in the
22400      * constructor of all subclasses
22401      * @method initFrame
22402      */
22403     initFrame: function() {
22404         this.createFrame();
22405     },
22406
22407     applyConfig: function() {
22408         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22409
22410         this.resizeFrame = (this.config.resizeFrame !== false);
22411         this.centerFrame = (this.config.centerFrame);
22412         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22413     },
22414
22415     /**
22416      * Resizes the drag frame to the dimensions of the clicked object, positions
22417      * it over the object, and finally displays it
22418      * @method showFrame
22419      * @param {int} iPageX X click position
22420      * @param {int} iPageY Y click position
22421      * @private
22422      */
22423     showFrame: function(iPageX, iPageY) {
22424         var el = this.getEl();
22425         var dragEl = this.getDragEl();
22426         var s = dragEl.style;
22427
22428         this._resizeProxy();
22429
22430         if (this.centerFrame) {
22431             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22432                            Math.round(parseInt(s.height, 10)/2) );
22433         }
22434
22435         this.setDragElPos(iPageX, iPageY);
22436
22437         Roo.fly(dragEl).show();
22438     },
22439
22440     /**
22441      * The proxy is automatically resized to the dimensions of the linked
22442      * element when a drag is initiated, unless resizeFrame is set to false
22443      * @method _resizeProxy
22444      * @private
22445      */
22446     _resizeProxy: function() {
22447         if (this.resizeFrame) {
22448             var el = this.getEl();
22449             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22450         }
22451     },
22452
22453     // overrides Roo.dd.DragDrop
22454     b4MouseDown: function(e) {
22455         var x = e.getPageX();
22456         var y = e.getPageY();
22457         this.autoOffset(x, y);
22458         this.setDragElPos(x, y);
22459     },
22460
22461     // overrides Roo.dd.DragDrop
22462     b4StartDrag: function(x, y) {
22463         // show the drag frame
22464         this.showFrame(x, y);
22465     },
22466
22467     // overrides Roo.dd.DragDrop
22468     b4EndDrag: function(e) {
22469         Roo.fly(this.getDragEl()).hide();
22470     },
22471
22472     // overrides Roo.dd.DragDrop
22473     // By default we try to move the element to the last location of the frame.
22474     // This is so that the default behavior mirrors that of Roo.dd.DD.
22475     endDrag: function(e) {
22476
22477         var lel = this.getEl();
22478         var del = this.getDragEl();
22479
22480         // Show the drag frame briefly so we can get its position
22481         del.style.visibility = "";
22482
22483         this.beforeMove();
22484         // Hide the linked element before the move to get around a Safari
22485         // rendering bug.
22486         lel.style.visibility = "hidden";
22487         Roo.dd.DDM.moveToEl(lel, del);
22488         del.style.visibility = "hidden";
22489         lel.style.visibility = "";
22490
22491         this.afterDrag();
22492     },
22493
22494     beforeMove : function(){
22495
22496     },
22497
22498     afterDrag : function(){
22499
22500     },
22501
22502     toString: function() {
22503         return ("DDProxy " + this.id);
22504     }
22505
22506 });
22507 /*
22508  * Based on:
22509  * Ext JS Library 1.1.1
22510  * Copyright(c) 2006-2007, Ext JS, LLC.
22511  *
22512  * Originally Released Under LGPL - original licence link has changed is not relivant.
22513  *
22514  * Fork - LGPL
22515  * <script type="text/javascript">
22516  */
22517
22518  /**
22519  * @class Roo.dd.DDTarget
22520  * A DragDrop implementation that does not move, but can be a drop
22521  * target.  You would get the same result by simply omitting implementation
22522  * for the event callbacks, but this way we reduce the processing cost of the
22523  * event listener and the callbacks.
22524  * @extends Roo.dd.DragDrop
22525  * @constructor
22526  * @param {String} id the id of the element that is a drop target
22527  * @param {String} sGroup the group of related DragDrop objects
22528  * @param {object} config an object containing configurable attributes
22529  *                 Valid properties for DDTarget in addition to those in
22530  *                 DragDrop:
22531  *                    none
22532  */
22533 Roo.dd.DDTarget = function(id, sGroup, config) {
22534     if (id) {
22535         this.initTarget(id, sGroup, config);
22536     }
22537     if (config && (config.listeners || config.events)) { 
22538         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22539             listeners : config.listeners || {}, 
22540             events : config.events || {} 
22541         });    
22542     }
22543 };
22544
22545 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22546 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22547     toString: function() {
22548         return ("DDTarget " + this.id);
22549     }
22550 });
22551 /*
22552  * Based on:
22553  * Ext JS Library 1.1.1
22554  * Copyright(c) 2006-2007, Ext JS, LLC.
22555  *
22556  * Originally Released Under LGPL - original licence link has changed is not relivant.
22557  *
22558  * Fork - LGPL
22559  * <script type="text/javascript">
22560  */
22561  
22562
22563 /**
22564  * @class Roo.dd.ScrollManager
22565  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22566  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22567  * @static
22568  */
22569 Roo.dd.ScrollManager = function(){
22570     var ddm = Roo.dd.DragDropMgr;
22571     var els = {};
22572     var dragEl = null;
22573     var proc = {};
22574     
22575     
22576     
22577     var onStop = function(e){
22578         dragEl = null;
22579         clearProc();
22580     };
22581     
22582     var triggerRefresh = function(){
22583         if(ddm.dragCurrent){
22584              ddm.refreshCache(ddm.dragCurrent.groups);
22585         }
22586     };
22587     
22588     var doScroll = function(){
22589         if(ddm.dragCurrent){
22590             var dds = Roo.dd.ScrollManager;
22591             if(!dds.animate){
22592                 if(proc.el.scroll(proc.dir, dds.increment)){
22593                     triggerRefresh();
22594                 }
22595             }else{
22596                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22597             }
22598         }
22599     };
22600     
22601     var clearProc = function(){
22602         if(proc.id){
22603             clearInterval(proc.id);
22604         }
22605         proc.id = 0;
22606         proc.el = null;
22607         proc.dir = "";
22608     };
22609     
22610     var startProc = function(el, dir){
22611          Roo.log('scroll startproc');
22612         clearProc();
22613         proc.el = el;
22614         proc.dir = dir;
22615         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22616     };
22617     
22618     var onFire = function(e, isDrop){
22619        
22620         if(isDrop || !ddm.dragCurrent){ return; }
22621         var dds = Roo.dd.ScrollManager;
22622         if(!dragEl || dragEl != ddm.dragCurrent){
22623             dragEl = ddm.dragCurrent;
22624             // refresh regions on drag start
22625             dds.refreshCache();
22626         }
22627         
22628         var xy = Roo.lib.Event.getXY(e);
22629         var pt = new Roo.lib.Point(xy[0], xy[1]);
22630         for(var id in els){
22631             var el = els[id], r = el._region;
22632             if(r && r.contains(pt) && el.isScrollable()){
22633                 if(r.bottom - pt.y <= dds.thresh){
22634                     if(proc.el != el){
22635                         startProc(el, "down");
22636                     }
22637                     return;
22638                 }else if(r.right - pt.x <= dds.thresh){
22639                     if(proc.el != el){
22640                         startProc(el, "left");
22641                     }
22642                     return;
22643                 }else if(pt.y - r.top <= dds.thresh){
22644                     if(proc.el != el){
22645                         startProc(el, "up");
22646                     }
22647                     return;
22648                 }else if(pt.x - r.left <= dds.thresh){
22649                     if(proc.el != el){
22650                         startProc(el, "right");
22651                     }
22652                     return;
22653                 }
22654             }
22655         }
22656         clearProc();
22657     };
22658     
22659     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22660     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22661     
22662     return {
22663         /**
22664          * Registers new overflow element(s) to auto scroll
22665          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22666          */
22667         register : function(el){
22668             if(el instanceof Array){
22669                 for(var i = 0, len = el.length; i < len; i++) {
22670                         this.register(el[i]);
22671                 }
22672             }else{
22673                 el = Roo.get(el);
22674                 els[el.id] = el;
22675             }
22676             Roo.dd.ScrollManager.els = els;
22677         },
22678         
22679         /**
22680          * Unregisters overflow element(s) so they are no longer scrolled
22681          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22682          */
22683         unregister : function(el){
22684             if(el instanceof Array){
22685                 for(var i = 0, len = el.length; i < len; i++) {
22686                         this.unregister(el[i]);
22687                 }
22688             }else{
22689                 el = Roo.get(el);
22690                 delete els[el.id];
22691             }
22692         },
22693         
22694         /**
22695          * The number of pixels from the edge of a container the pointer needs to be to 
22696          * trigger scrolling (defaults to 25)
22697          * @type Number
22698          */
22699         thresh : 25,
22700         
22701         /**
22702          * The number of pixels to scroll in each scroll increment (defaults to 50)
22703          * @type Number
22704          */
22705         increment : 100,
22706         
22707         /**
22708          * The frequency of scrolls in milliseconds (defaults to 500)
22709          * @type Number
22710          */
22711         frequency : 500,
22712         
22713         /**
22714          * True to animate the scroll (defaults to true)
22715          * @type Boolean
22716          */
22717         animate: true,
22718         
22719         /**
22720          * The animation duration in seconds - 
22721          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22722          * @type Number
22723          */
22724         animDuration: .4,
22725         
22726         /**
22727          * Manually trigger a cache refresh.
22728          */
22729         refreshCache : function(){
22730             for(var id in els){
22731                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22732                     els[id]._region = els[id].getRegion();
22733                 }
22734             }
22735         }
22736     };
22737 }();/*
22738  * Based on:
22739  * Ext JS Library 1.1.1
22740  * Copyright(c) 2006-2007, Ext JS, LLC.
22741  *
22742  * Originally Released Under LGPL - original licence link has changed is not relivant.
22743  *
22744  * Fork - LGPL
22745  * <script type="text/javascript">
22746  */
22747  
22748
22749 /**
22750  * @class Roo.dd.Registry
22751  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22752  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22753  * @static
22754  */
22755 Roo.dd.Registry = function(){
22756     var elements = {}; 
22757     var handles = {}; 
22758     var autoIdSeed = 0;
22759
22760     var getId = function(el, autogen){
22761         if(typeof el == "string"){
22762             return el;
22763         }
22764         var id = el.id;
22765         if(!id && autogen !== false){
22766             id = "roodd-" + (++autoIdSeed);
22767             el.id = id;
22768         }
22769         return id;
22770     };
22771     
22772     return {
22773     /**
22774      * Register a drag drop element
22775      * @param {String|HTMLElement} element The id or DOM node to register
22776      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22777      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22778      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22779      * populated in the data object (if applicable):
22780      * <pre>
22781 Value      Description<br />
22782 ---------  ------------------------------------------<br />
22783 handles    Array of DOM nodes that trigger dragging<br />
22784            for the element being registered<br />
22785 isHandle   True if the element passed in triggers<br />
22786            dragging itself, else false
22787 </pre>
22788      */
22789         register : function(el, data){
22790             data = data || {};
22791             if(typeof el == "string"){
22792                 el = document.getElementById(el);
22793             }
22794             data.ddel = el;
22795             elements[getId(el)] = data;
22796             if(data.isHandle !== false){
22797                 handles[data.ddel.id] = data;
22798             }
22799             if(data.handles){
22800                 var hs = data.handles;
22801                 for(var i = 0, len = hs.length; i < len; i++){
22802                         handles[getId(hs[i])] = data;
22803                 }
22804             }
22805         },
22806
22807     /**
22808      * Unregister a drag drop element
22809      * @param {String|HTMLElement}  element The id or DOM node to unregister
22810      */
22811         unregister : function(el){
22812             var id = getId(el, false);
22813             var data = elements[id];
22814             if(data){
22815                 delete elements[id];
22816                 if(data.handles){
22817                     var hs = data.handles;
22818                     for(var i = 0, len = hs.length; i < len; i++){
22819                         delete handles[getId(hs[i], false)];
22820                     }
22821                 }
22822             }
22823         },
22824
22825     /**
22826      * Returns the handle registered for a DOM Node by id
22827      * @param {String|HTMLElement} id The DOM node or id to look up
22828      * @return {Object} handle The custom handle data
22829      */
22830         getHandle : function(id){
22831             if(typeof id != "string"){ // must be element?
22832                 id = id.id;
22833             }
22834             return handles[id];
22835         },
22836
22837     /**
22838      * Returns the handle that is registered for the DOM node that is the target of the event
22839      * @param {Event} e The event
22840      * @return {Object} handle The custom handle data
22841      */
22842         getHandleFromEvent : function(e){
22843             var t = Roo.lib.Event.getTarget(e);
22844             return t ? handles[t.id] : null;
22845         },
22846
22847     /**
22848      * Returns a custom data object that is registered for a DOM node by id
22849      * @param {String|HTMLElement} id The DOM node or id to look up
22850      * @return {Object} data The custom data
22851      */
22852         getTarget : function(id){
22853             if(typeof id != "string"){ // must be element?
22854                 id = id.id;
22855             }
22856             return elements[id];
22857         },
22858
22859     /**
22860      * Returns a custom data object that is registered for the DOM node that is the target of the event
22861      * @param {Event} e The event
22862      * @return {Object} data The custom data
22863      */
22864         getTargetFromEvent : function(e){
22865             var t = Roo.lib.Event.getTarget(e);
22866             return t ? elements[t.id] || handles[t.id] : null;
22867         }
22868     };
22869 }();/*
22870  * Based on:
22871  * Ext JS Library 1.1.1
22872  * Copyright(c) 2006-2007, Ext JS, LLC.
22873  *
22874  * Originally Released Under LGPL - original licence link has changed is not relivant.
22875  *
22876  * Fork - LGPL
22877  * <script type="text/javascript">
22878  */
22879  
22880
22881 /**
22882  * @class Roo.dd.StatusProxy
22883  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22884  * default drag proxy used by all Roo.dd components.
22885  * @constructor
22886  * @param {Object} config
22887  */
22888 Roo.dd.StatusProxy = function(config){
22889     Roo.apply(this, config);
22890     this.id = this.id || Roo.id();
22891     this.el = new Roo.Layer({
22892         dh: {
22893             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22894                 {tag: "div", cls: "x-dd-drop-icon"},
22895                 {tag: "div", cls: "x-dd-drag-ghost"}
22896             ]
22897         }, 
22898         shadow: !config || config.shadow !== false
22899     });
22900     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22901     this.dropStatus = this.dropNotAllowed;
22902 };
22903
22904 Roo.dd.StatusProxy.prototype = {
22905     /**
22906      * @cfg {String} dropAllowed
22907      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22908      */
22909     dropAllowed : "x-dd-drop-ok",
22910     /**
22911      * @cfg {String} dropNotAllowed
22912      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22913      */
22914     dropNotAllowed : "x-dd-drop-nodrop",
22915
22916     /**
22917      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22918      * over the current target element.
22919      * @param {String} cssClass The css class for the new drop status indicator image
22920      */
22921     setStatus : function(cssClass){
22922         cssClass = cssClass || this.dropNotAllowed;
22923         if(this.dropStatus != cssClass){
22924             this.el.replaceClass(this.dropStatus, cssClass);
22925             this.dropStatus = cssClass;
22926         }
22927     },
22928
22929     /**
22930      * Resets the status indicator to the default dropNotAllowed value
22931      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22932      */
22933     reset : function(clearGhost){
22934         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22935         this.dropStatus = this.dropNotAllowed;
22936         if(clearGhost){
22937             this.ghost.update("");
22938         }
22939     },
22940
22941     /**
22942      * Updates the contents of the ghost element
22943      * @param {String} html The html that will replace the current innerHTML of the ghost element
22944      */
22945     update : function(html){
22946         if(typeof html == "string"){
22947             this.ghost.update(html);
22948         }else{
22949             this.ghost.update("");
22950             html.style.margin = "0";
22951             this.ghost.dom.appendChild(html);
22952         }
22953         // ensure float = none set?? cant remember why though.
22954         var el = this.ghost.dom.firstChild;
22955                 if(el){
22956                         Roo.fly(el).setStyle('float', 'none');
22957                 }
22958     },
22959     
22960     /**
22961      * Returns the underlying proxy {@link Roo.Layer}
22962      * @return {Roo.Layer} el
22963     */
22964     getEl : function(){
22965         return this.el;
22966     },
22967
22968     /**
22969      * Returns the ghost element
22970      * @return {Roo.Element} el
22971      */
22972     getGhost : function(){
22973         return this.ghost;
22974     },
22975
22976     /**
22977      * Hides the proxy
22978      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22979      */
22980     hide : function(clear){
22981         this.el.hide();
22982         if(clear){
22983             this.reset(true);
22984         }
22985     },
22986
22987     /**
22988      * Stops the repair animation if it's currently running
22989      */
22990     stop : function(){
22991         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22992             this.anim.stop();
22993         }
22994     },
22995
22996     /**
22997      * Displays this proxy
22998      */
22999     show : function(){
23000         this.el.show();
23001     },
23002
23003     /**
23004      * Force the Layer to sync its shadow and shim positions to the element
23005      */
23006     sync : function(){
23007         this.el.sync();
23008     },
23009
23010     /**
23011      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23012      * invalid drop operation by the item being dragged.
23013      * @param {Array} xy The XY position of the element ([x, y])
23014      * @param {Function} callback The function to call after the repair is complete
23015      * @param {Object} scope The scope in which to execute the callback
23016      */
23017     repair : function(xy, callback, scope){
23018         this.callback = callback;
23019         this.scope = scope;
23020         if(xy && this.animRepair !== false){
23021             this.el.addClass("x-dd-drag-repair");
23022             this.el.hideUnders(true);
23023             this.anim = this.el.shift({
23024                 duration: this.repairDuration || .5,
23025                 easing: 'easeOut',
23026                 xy: xy,
23027                 stopFx: true,
23028                 callback: this.afterRepair,
23029                 scope: this
23030             });
23031         }else{
23032             this.afterRepair();
23033         }
23034     },
23035
23036     // private
23037     afterRepair : function(){
23038         this.hide(true);
23039         if(typeof this.callback == "function"){
23040             this.callback.call(this.scope || this);
23041         }
23042         this.callback = null;
23043         this.scope = null;
23044     }
23045 };/*
23046  * Based on:
23047  * Ext JS Library 1.1.1
23048  * Copyright(c) 2006-2007, Ext JS, LLC.
23049  *
23050  * Originally Released Under LGPL - original licence link has changed is not relivant.
23051  *
23052  * Fork - LGPL
23053  * <script type="text/javascript">
23054  */
23055
23056 /**
23057  * @class Roo.dd.DragSource
23058  * @extends Roo.dd.DDProxy
23059  * A simple class that provides the basic implementation needed to make any element draggable.
23060  * @constructor
23061  * @param {String/HTMLElement/Element} el The container element
23062  * @param {Object} config
23063  */
23064 Roo.dd.DragSource = function(el, config){
23065     this.el = Roo.get(el);
23066     this.dragData = {};
23067     
23068     Roo.apply(this, config);
23069     
23070     if(!this.proxy){
23071         this.proxy = new Roo.dd.StatusProxy();
23072     }
23073
23074     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23075           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23076     
23077     this.dragging = false;
23078 };
23079
23080 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23081     /**
23082      * @cfg {String} dropAllowed
23083      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23084      */
23085     dropAllowed : "x-dd-drop-ok",
23086     /**
23087      * @cfg {String} dropNotAllowed
23088      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23089      */
23090     dropNotAllowed : "x-dd-drop-nodrop",
23091
23092     /**
23093      * Returns the data object associated with this drag source
23094      * @return {Object} data An object containing arbitrary data
23095      */
23096     getDragData : function(e){
23097         return this.dragData;
23098     },
23099
23100     // private
23101     onDragEnter : function(e, id){
23102         var target = Roo.dd.DragDropMgr.getDDById(id);
23103         this.cachedTarget = target;
23104         if(this.beforeDragEnter(target, e, id) !== false){
23105             if(target.isNotifyTarget){
23106                 var status = target.notifyEnter(this, e, this.dragData);
23107                 this.proxy.setStatus(status);
23108             }else{
23109                 this.proxy.setStatus(this.dropAllowed);
23110             }
23111             
23112             if(this.afterDragEnter){
23113                 /**
23114                  * An empty function by default, but provided so that you can perform a custom action
23115                  * when the dragged item enters the drop target by providing an implementation.
23116                  * @param {Roo.dd.DragDrop} target The drop target
23117                  * @param {Event} e The event object
23118                  * @param {String} id The id of the dragged element
23119                  * @method afterDragEnter
23120                  */
23121                 this.afterDragEnter(target, e, id);
23122             }
23123         }
23124     },
23125
23126     /**
23127      * An empty function by default, but provided so that you can perform a custom action
23128      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23129      * @param {Roo.dd.DragDrop} target The drop target
23130      * @param {Event} e The event object
23131      * @param {String} id The id of the dragged element
23132      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23133      */
23134     beforeDragEnter : function(target, e, id){
23135         return true;
23136     },
23137
23138     // private
23139     alignElWithMouse: function() {
23140         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23141         this.proxy.sync();
23142     },
23143
23144     // private
23145     onDragOver : function(e, id){
23146         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23147         if(this.beforeDragOver(target, e, id) !== false){
23148             if(target.isNotifyTarget){
23149                 var status = target.notifyOver(this, e, this.dragData);
23150                 this.proxy.setStatus(status);
23151             }
23152
23153             if(this.afterDragOver){
23154                 /**
23155                  * An empty function by default, but provided so that you can perform a custom action
23156                  * while the dragged item is over the drop target by providing an implementation.
23157                  * @param {Roo.dd.DragDrop} target The drop target
23158                  * @param {Event} e The event object
23159                  * @param {String} id The id of the dragged element
23160                  * @method afterDragOver
23161                  */
23162                 this.afterDragOver(target, e, id);
23163             }
23164         }
23165     },
23166
23167     /**
23168      * An empty function by default, but provided so that you can perform a custom action
23169      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23170      * @param {Roo.dd.DragDrop} target The drop target
23171      * @param {Event} e The event object
23172      * @param {String} id The id of the dragged element
23173      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23174      */
23175     beforeDragOver : function(target, e, id){
23176         return true;
23177     },
23178
23179     // private
23180     onDragOut : function(e, id){
23181         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23182         if(this.beforeDragOut(target, e, id) !== false){
23183             if(target.isNotifyTarget){
23184                 target.notifyOut(this, e, this.dragData);
23185             }
23186             this.proxy.reset();
23187             if(this.afterDragOut){
23188                 /**
23189                  * An empty function by default, but provided so that you can perform a custom action
23190                  * after the dragged item is dragged out of the target without dropping.
23191                  * @param {Roo.dd.DragDrop} target The drop target
23192                  * @param {Event} e The event object
23193                  * @param {String} id The id of the dragged element
23194                  * @method afterDragOut
23195                  */
23196                 this.afterDragOut(target, e, id);
23197             }
23198         }
23199         this.cachedTarget = null;
23200     },
23201
23202     /**
23203      * An empty function by default, but provided so that you can perform a custom action before the dragged
23204      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23205      * @param {Roo.dd.DragDrop} target The drop target
23206      * @param {Event} e The event object
23207      * @param {String} id The id of the dragged element
23208      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23209      */
23210     beforeDragOut : function(target, e, id){
23211         return true;
23212     },
23213     
23214     // private
23215     onDragDrop : function(e, id){
23216         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23217         if(this.beforeDragDrop(target, e, id) !== false){
23218             if(target.isNotifyTarget){
23219                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23220                     this.onValidDrop(target, e, id);
23221                 }else{
23222                     this.onInvalidDrop(target, e, id);
23223                 }
23224             }else{
23225                 this.onValidDrop(target, e, id);
23226             }
23227             
23228             if(this.afterDragDrop){
23229                 /**
23230                  * An empty function by default, but provided so that you can perform a custom action
23231                  * after a valid drag drop has occurred by providing an implementation.
23232                  * @param {Roo.dd.DragDrop} target The drop target
23233                  * @param {Event} e The event object
23234                  * @param {String} id The id of the dropped element
23235                  * @method afterDragDrop
23236                  */
23237                 this.afterDragDrop(target, e, id);
23238             }
23239         }
23240         delete this.cachedTarget;
23241     },
23242
23243     /**
23244      * An empty function by default, but provided so that you can perform a custom action before the dragged
23245      * item is dropped onto the target and optionally cancel the onDragDrop.
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 drop event is valid, else false to cancel
23250      */
23251     beforeDragDrop : function(target, e, id){
23252         return true;
23253     },
23254
23255     // private
23256     onValidDrop : function(target, e, id){
23257         this.hideProxy();
23258         if(this.afterValidDrop){
23259             /**
23260              * An empty function by default, but provided so that you can perform a custom action
23261              * after a valid drop has occurred by providing an implementation.
23262              * @param {Object} target The target DD 
23263              * @param {Event} e The event object
23264              * @param {String} id The id of the dropped element
23265              * @method afterInvalidDrop
23266              */
23267             this.afterValidDrop(target, e, id);
23268         }
23269     },
23270
23271     // private
23272     getRepairXY : function(e, data){
23273         return this.el.getXY();  
23274     },
23275
23276     // private
23277     onInvalidDrop : function(target, e, id){
23278         this.beforeInvalidDrop(target, e, id);
23279         if(this.cachedTarget){
23280             if(this.cachedTarget.isNotifyTarget){
23281                 this.cachedTarget.notifyOut(this, e, this.dragData);
23282             }
23283             this.cacheTarget = null;
23284         }
23285         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23286
23287         if(this.afterInvalidDrop){
23288             /**
23289              * An empty function by default, but provided so that you can perform a custom action
23290              * after an invalid drop has occurred by providing an implementation.
23291              * @param {Event} e The event object
23292              * @param {String} id The id of the dropped element
23293              * @method afterInvalidDrop
23294              */
23295             this.afterInvalidDrop(e, id);
23296         }
23297     },
23298
23299     // private
23300     afterRepair : function(){
23301         if(Roo.enableFx){
23302             this.el.highlight(this.hlColor || "c3daf9");
23303         }
23304         this.dragging = false;
23305     },
23306
23307     /**
23308      * An empty function by default, but provided so that you can perform a custom action after an invalid
23309      * drop has occurred.
23310      * @param {Roo.dd.DragDrop} target The drop target
23311      * @param {Event} e The event object
23312      * @param {String} id The id of the dragged element
23313      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23314      */
23315     beforeInvalidDrop : function(target, e, id){
23316         return true;
23317     },
23318
23319     // private
23320     handleMouseDown : function(e){
23321         if(this.dragging) {
23322             return;
23323         }
23324         var data = this.getDragData(e);
23325         if(data && this.onBeforeDrag(data, e) !== false){
23326             this.dragData = data;
23327             this.proxy.stop();
23328             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23329         } 
23330     },
23331
23332     /**
23333      * An empty function by default, but provided so that you can perform a custom action before the initial
23334      * drag event begins and optionally cancel it.
23335      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23336      * @param {Event} e The event object
23337      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23338      */
23339     onBeforeDrag : function(data, e){
23340         return true;
23341     },
23342
23343     /**
23344      * An empty function by default, but provided so that you can perform a custom action once the initial
23345      * drag event has begun.  The drag cannot be canceled from this function.
23346      * @param {Number} x The x position of the click on the dragged object
23347      * @param {Number} y The y position of the click on the dragged object
23348      */
23349     onStartDrag : Roo.emptyFn,
23350
23351     // private - YUI override
23352     startDrag : function(x, y){
23353         this.proxy.reset();
23354         this.dragging = true;
23355         this.proxy.update("");
23356         this.onInitDrag(x, y);
23357         this.proxy.show();
23358     },
23359
23360     // private
23361     onInitDrag : function(x, y){
23362         var clone = this.el.dom.cloneNode(true);
23363         clone.id = Roo.id(); // prevent duplicate ids
23364         this.proxy.update(clone);
23365         this.onStartDrag(x, y);
23366         return true;
23367     },
23368
23369     /**
23370      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23371      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23372      */
23373     getProxy : function(){
23374         return this.proxy;  
23375     },
23376
23377     /**
23378      * Hides the drag source's {@link Roo.dd.StatusProxy}
23379      */
23380     hideProxy : function(){
23381         this.proxy.hide();  
23382         this.proxy.reset(true);
23383         this.dragging = false;
23384     },
23385
23386     // private
23387     triggerCacheRefresh : function(){
23388         Roo.dd.DDM.refreshCache(this.groups);
23389     },
23390
23391     // private - override to prevent hiding
23392     b4EndDrag: function(e) {
23393     },
23394
23395     // private - override to prevent moving
23396     endDrag : function(e){
23397         this.onEndDrag(this.dragData, e);
23398     },
23399
23400     // private
23401     onEndDrag : function(data, e){
23402     },
23403     
23404     // private - pin to cursor
23405     autoOffset : function(x, y) {
23406         this.setDelta(-12, -20);
23407     }    
23408 });/*
23409  * Based on:
23410  * Ext JS Library 1.1.1
23411  * Copyright(c) 2006-2007, Ext JS, LLC.
23412  *
23413  * Originally Released Under LGPL - original licence link has changed is not relivant.
23414  *
23415  * Fork - LGPL
23416  * <script type="text/javascript">
23417  */
23418
23419
23420 /**
23421  * @class Roo.dd.DropTarget
23422  * @extends Roo.dd.DDTarget
23423  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23424  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23425  * @constructor
23426  * @param {String/HTMLElement/Element} el The container element
23427  * @param {Object} config
23428  */
23429 Roo.dd.DropTarget = function(el, config){
23430     this.el = Roo.get(el);
23431     
23432     var listeners = false; ;
23433     if (config && config.listeners) {
23434         listeners= config.listeners;
23435         delete config.listeners;
23436     }
23437     Roo.apply(this, config);
23438     
23439     if(this.containerScroll){
23440         Roo.dd.ScrollManager.register(this.el);
23441     }
23442     this.addEvents( {
23443          /**
23444          * @scope Roo.dd.DropTarget
23445          */
23446          
23447          /**
23448          * @event enter
23449          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23450          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23451          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23452          * 
23453          * IMPORTANT : it should set  this.valid to true|false
23454          * 
23455          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23456          * @param {Event} e The event
23457          * @param {Object} data An object containing arbitrary data supplied by the drag source
23458          */
23459         "enter" : true,
23460         
23461          /**
23462          * @event over
23463          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23464          * This method will be called on every mouse movement while the drag source is over the drop target.
23465          * This default implementation simply returns the dropAllowed config value.
23466          * 
23467          * IMPORTANT : it should set  this.valid to true|false
23468          * 
23469          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23470          * @param {Event} e The event
23471          * @param {Object} data An object containing arbitrary data supplied by the drag source
23472          
23473          */
23474         "over" : true,
23475         /**
23476          * @event out
23477          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23478          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23479          * overClass (if any) from the drop element.
23480          * 
23481          * 
23482          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23483          * @param {Event} e The event
23484          * @param {Object} data An object containing arbitrary data supplied by the drag source
23485          */
23486          "out" : true,
23487          
23488         /**
23489          * @event drop
23490          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23491          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23492          * implementation that does something to process the drop event and returns true so that the drag source's
23493          * repair action does not run.
23494          * 
23495          * IMPORTANT : it should set this.success
23496          * 
23497          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23498          * @param {Event} e The event
23499          * @param {Object} data An object containing arbitrary data supplied by the drag source
23500         */
23501          "drop" : true
23502     });
23503             
23504      
23505     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23506         this.el.dom, 
23507         this.ddGroup || this.group,
23508         {
23509             isTarget: true,
23510             listeners : listeners || {} 
23511            
23512         
23513         }
23514     );
23515
23516 };
23517
23518 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23519     /**
23520      * @cfg {String} overClass
23521      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23522      */
23523      /**
23524      * @cfg {String} ddGroup
23525      * The drag drop group to handle drop events for
23526      */
23527      
23528     /**
23529      * @cfg {String} dropAllowed
23530      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23531      */
23532     dropAllowed : "x-dd-drop-ok",
23533     /**
23534      * @cfg {String} dropNotAllowed
23535      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23536      */
23537     dropNotAllowed : "x-dd-drop-nodrop",
23538     /**
23539      * @cfg {boolean} success
23540      * set this after drop listener.. 
23541      */
23542     success : false,
23543     /**
23544      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23545      * if the drop point is valid for over/enter..
23546      */
23547     valid : false,
23548     // private
23549     isTarget : true,
23550
23551     // private
23552     isNotifyTarget : true,
23553     
23554     /**
23555      * @hide
23556      */
23557     notifyEnter : function(dd, e, data)
23558     {
23559         this.valid = true;
23560         this.fireEvent('enter', dd, e, data);
23561         if(this.overClass){
23562             this.el.addClass(this.overClass);
23563         }
23564         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23565             this.valid ? this.dropAllowed : this.dropNotAllowed
23566         );
23567     },
23568
23569     /**
23570      * @hide
23571      */
23572     notifyOver : function(dd, e, data)
23573     {
23574         this.valid = true;
23575         this.fireEvent('over', dd, e, data);
23576         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23577             this.valid ? this.dropAllowed : this.dropNotAllowed
23578         );
23579     },
23580
23581     /**
23582      * @hide
23583      */
23584     notifyOut : function(dd, e, data)
23585     {
23586         this.fireEvent('out', dd, e, data);
23587         if(this.overClass){
23588             this.el.removeClass(this.overClass);
23589         }
23590     },
23591
23592     /**
23593      * @hide
23594      */
23595     notifyDrop : function(dd, e, data)
23596     {
23597         this.success = false;
23598         this.fireEvent('drop', dd, e, data);
23599         return this.success;
23600     }
23601 });/*
23602  * Based on:
23603  * Ext JS Library 1.1.1
23604  * Copyright(c) 2006-2007, Ext JS, LLC.
23605  *
23606  * Originally Released Under LGPL - original licence link has changed is not relivant.
23607  *
23608  * Fork - LGPL
23609  * <script type="text/javascript">
23610  */
23611
23612
23613 /**
23614  * @class Roo.dd.DragZone
23615  * @extends Roo.dd.DragSource
23616  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23617  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23618  * @constructor
23619  * @param {String/HTMLElement/Element} el The container element
23620  * @param {Object} config
23621  */
23622 Roo.dd.DragZone = function(el, config){
23623     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23624     if(this.containerScroll){
23625         Roo.dd.ScrollManager.register(this.el);
23626     }
23627 };
23628
23629 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23630     /**
23631      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23632      * for auto scrolling during drag operations.
23633      */
23634     /**
23635      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23636      * method after a failed drop (defaults to "c3daf9" - light blue)
23637      */
23638
23639     /**
23640      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23641      * for a valid target to drag based on the mouse down. Override this method
23642      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23643      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23644      * @param {EventObject} e The mouse down event
23645      * @return {Object} The dragData
23646      */
23647     getDragData : function(e){
23648         return Roo.dd.Registry.getHandleFromEvent(e);
23649     },
23650     
23651     /**
23652      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23653      * this.dragData.ddel
23654      * @param {Number} x The x position of the click on the dragged object
23655      * @param {Number} y The y position of the click on the dragged object
23656      * @return {Boolean} true to continue the drag, false to cancel
23657      */
23658     onInitDrag : function(x, y){
23659         this.proxy.update(this.dragData.ddel.cloneNode(true));
23660         this.onStartDrag(x, y);
23661         return true;
23662     },
23663     
23664     /**
23665      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23666      */
23667     afterRepair : function(){
23668         if(Roo.enableFx){
23669             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23670         }
23671         this.dragging = false;
23672     },
23673
23674     /**
23675      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23676      * the XY of this.dragData.ddel
23677      * @param {EventObject} e The mouse up event
23678      * @return {Array} The xy location (e.g. [100, 200])
23679      */
23680     getRepairXY : function(e){
23681         return Roo.Element.fly(this.dragData.ddel).getXY();  
23682     }
23683 });/*
23684  * Based on:
23685  * Ext JS Library 1.1.1
23686  * Copyright(c) 2006-2007, Ext JS, LLC.
23687  *
23688  * Originally Released Under LGPL - original licence link has changed is not relivant.
23689  *
23690  * Fork - LGPL
23691  * <script type="text/javascript">
23692  */
23693 /**
23694  * @class Roo.dd.DropZone
23695  * @extends Roo.dd.DropTarget
23696  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23697  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23698  * @constructor
23699  * @param {String/HTMLElement/Element} el The container element
23700  * @param {Object} config
23701  */
23702 Roo.dd.DropZone = function(el, config){
23703     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23704 };
23705
23706 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23707     /**
23708      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23709      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23710      * provide your own custom lookup.
23711      * @param {Event} e The event
23712      * @return {Object} data The custom data
23713      */
23714     getTargetFromEvent : function(e){
23715         return Roo.dd.Registry.getTargetFromEvent(e);
23716     },
23717
23718     /**
23719      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23720      * that it has registered.  This method has no default implementation and should be overridden to provide
23721      * node-specific processing if necessary.
23722      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23723      * {@link #getTargetFromEvent} for this node)
23724      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23725      * @param {Event} e The event
23726      * @param {Object} data An object containing arbitrary data supplied by the drag source
23727      */
23728     onNodeEnter : function(n, dd, e, data){
23729         
23730     },
23731
23732     /**
23733      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23734      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23735      * overridden to provide the proper feedback.
23736      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23737      * {@link #getTargetFromEvent} for this node)
23738      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23739      * @param {Event} e The event
23740      * @param {Object} data An object containing arbitrary data supplied by the drag source
23741      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23742      * underlying {@link Roo.dd.StatusProxy} can be updated
23743      */
23744     onNodeOver : function(n, dd, e, data){
23745         return this.dropAllowed;
23746     },
23747
23748     /**
23749      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23750      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23751      * node-specific processing if necessary.
23752      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23753      * {@link #getTargetFromEvent} for this node)
23754      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23755      * @param {Event} e The event
23756      * @param {Object} data An object containing arbitrary data supplied by the drag source
23757      */
23758     onNodeOut : function(n, dd, e, data){
23759         
23760     },
23761
23762     /**
23763      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23764      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23765      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23766      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23767      * {@link #getTargetFromEvent} for this node)
23768      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23769      * @param {Event} e The event
23770      * @param {Object} data An object containing arbitrary data supplied by the drag source
23771      * @return {Boolean} True if the drop was valid, else false
23772      */
23773     onNodeDrop : function(n, dd, e, data){
23774         return false;
23775     },
23776
23777     /**
23778      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23779      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23780      * it should be overridden to provide the proper feedback if necessary.
23781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23782      * @param {Event} e The event
23783      * @param {Object} data An object containing arbitrary data supplied by the drag source
23784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23785      * underlying {@link Roo.dd.StatusProxy} can be updated
23786      */
23787     onContainerOver : function(dd, e, data){
23788         return this.dropNotAllowed;
23789     },
23790
23791     /**
23792      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23793      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23794      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23795      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23796      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23797      * @param {Event} e The event
23798      * @param {Object} data An object containing arbitrary data supplied by the drag source
23799      * @return {Boolean} True if the drop was valid, else false
23800      */
23801     onContainerDrop : function(dd, e, data){
23802         return false;
23803     },
23804
23805     /**
23806      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23807      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23808      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23809      * you should override this method and provide a custom implementation.
23810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23811      * @param {Event} e The event
23812      * @param {Object} data An object containing arbitrary data supplied by the drag source
23813      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23814      * underlying {@link Roo.dd.StatusProxy} can be updated
23815      */
23816     notifyEnter : function(dd, e, data){
23817         return this.dropNotAllowed;
23818     },
23819
23820     /**
23821      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23822      * This method will be called on every mouse movement while the drag source is over the drop zone.
23823      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23824      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23825      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23826      * registered node, it will call {@link #onContainerOver}.
23827      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23828      * @param {Event} e The event
23829      * @param {Object} data An object containing arbitrary data supplied by the drag source
23830      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23831      * underlying {@link Roo.dd.StatusProxy} can be updated
23832      */
23833     notifyOver : function(dd, e, data){
23834         var n = this.getTargetFromEvent(e);
23835         if(!n){ // not over valid drop target
23836             if(this.lastOverNode){
23837                 this.onNodeOut(this.lastOverNode, dd, e, data);
23838                 this.lastOverNode = null;
23839             }
23840             return this.onContainerOver(dd, e, data);
23841         }
23842         if(this.lastOverNode != n){
23843             if(this.lastOverNode){
23844                 this.onNodeOut(this.lastOverNode, dd, e, data);
23845             }
23846             this.onNodeEnter(n, dd, e, data);
23847             this.lastOverNode = n;
23848         }
23849         return this.onNodeOver(n, dd, e, data);
23850     },
23851
23852     /**
23853      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23854      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23855      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23856      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23857      * @param {Event} e The event
23858      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23859      */
23860     notifyOut : function(dd, e, data){
23861         if(this.lastOverNode){
23862             this.onNodeOut(this.lastOverNode, dd, e, data);
23863             this.lastOverNode = null;
23864         }
23865     },
23866
23867     /**
23868      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23869      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23870      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23871      * otherwise it will call {@link #onContainerDrop}.
23872      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23873      * @param {Event} e The event
23874      * @param {Object} data An object containing arbitrary data supplied by the drag source
23875      * @return {Boolean} True if the drop was valid, else false
23876      */
23877     notifyDrop : function(dd, e, data){
23878         if(this.lastOverNode){
23879             this.onNodeOut(this.lastOverNode, dd, e, data);
23880             this.lastOverNode = null;
23881         }
23882         var n = this.getTargetFromEvent(e);
23883         return n ?
23884             this.onNodeDrop(n, dd, e, data) :
23885             this.onContainerDrop(dd, e, data);
23886     },
23887
23888     // private
23889     triggerCacheRefresh : function(){
23890         Roo.dd.DDM.refreshCache(this.groups);
23891     }  
23892 });/*
23893  * Based on:
23894  * Ext JS Library 1.1.1
23895  * Copyright(c) 2006-2007, Ext JS, LLC.
23896  *
23897  * Originally Released Under LGPL - original licence link has changed is not relivant.
23898  *
23899  * Fork - LGPL
23900  * <script type="text/javascript">
23901  */
23902
23903
23904 /**
23905  * @class Roo.data.SortTypes
23906  * @static
23907  * Defines the default sorting (casting?) comparison functions used when sorting data.
23908  */
23909 Roo.data.SortTypes = {
23910     /**
23911      * Default sort that does nothing
23912      * @param {Mixed} s The value being converted
23913      * @return {Mixed} The comparison value
23914      */
23915     none : function(s){
23916         return s;
23917     },
23918     
23919     /**
23920      * The regular expression used to strip tags
23921      * @type {RegExp}
23922      * @property
23923      */
23924     stripTagsRE : /<\/?[^>]+>/gi,
23925     
23926     /**
23927      * Strips all HTML tags to sort on text only
23928      * @param {Mixed} s The value being converted
23929      * @return {String} The comparison value
23930      */
23931     asText : function(s){
23932         return String(s).replace(this.stripTagsRE, "");
23933     },
23934     
23935     /**
23936      * Strips all HTML tags to sort on text only - Case insensitive
23937      * @param {Mixed} s The value being converted
23938      * @return {String} The comparison value
23939      */
23940     asUCText : function(s){
23941         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23942     },
23943     
23944     /**
23945      * Case insensitive string
23946      * @param {Mixed} s The value being converted
23947      * @return {String} The comparison value
23948      */
23949     asUCString : function(s) {
23950         return String(s).toUpperCase();
23951     },
23952     
23953     /**
23954      * Date sorting
23955      * @param {Mixed} s The value being converted
23956      * @return {Number} The comparison value
23957      */
23958     asDate : function(s) {
23959         if(!s){
23960             return 0;
23961         }
23962         if(s instanceof Date){
23963             return s.getTime();
23964         }
23965         return Date.parse(String(s));
23966     },
23967     
23968     /**
23969      * Float sorting
23970      * @param {Mixed} s The value being converted
23971      * @return {Float} The comparison value
23972      */
23973     asFloat : function(s) {
23974         var val = parseFloat(String(s).replace(/,/g, ""));
23975         if(isNaN(val)) {
23976             val = 0;
23977         }
23978         return val;
23979     },
23980     
23981     /**
23982      * Integer sorting
23983      * @param {Mixed} s The value being converted
23984      * @return {Number} The comparison value
23985      */
23986     asInt : function(s) {
23987         var val = parseInt(String(s).replace(/,/g, ""));
23988         if(isNaN(val)) {
23989             val = 0;
23990         }
23991         return val;
23992     }
23993 };/*
23994  * Based on:
23995  * Ext JS Library 1.1.1
23996  * Copyright(c) 2006-2007, Ext JS, LLC.
23997  *
23998  * Originally Released Under LGPL - original licence link has changed is not relivant.
23999  *
24000  * Fork - LGPL
24001  * <script type="text/javascript">
24002  */
24003
24004 /**
24005 * @class Roo.data.Record
24006  * Instances of this class encapsulate both record <em>definition</em> information, and record
24007  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24008  * to access Records cached in an {@link Roo.data.Store} object.<br>
24009  * <p>
24010  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24011  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24012  * objects.<br>
24013  * <p>
24014  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24015  * @constructor
24016  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24017  * {@link #create}. The parameters are the same.
24018  * @param {Array} data An associative Array of data values keyed by the field name.
24019  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24020  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24021  * not specified an integer id is generated.
24022  */
24023 Roo.data.Record = function(data, id){
24024     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24025     this.data = data;
24026 };
24027
24028 /**
24029  * Generate a constructor for a specific record layout.
24030  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24031  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24032  * Each field definition object may contain the following properties: <ul>
24033  * <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,
24034  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24035  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24036  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24037  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24038  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24039  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24040  * this may be omitted.</p></li>
24041  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24042  * <ul><li>auto (Default, implies no conversion)</li>
24043  * <li>string</li>
24044  * <li>int</li>
24045  * <li>float</li>
24046  * <li>boolean</li>
24047  * <li>date</li></ul></p></li>
24048  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24049  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24050  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24051  * by the Reader into an object that will be stored in the Record. It is passed the
24052  * following parameters:<ul>
24053  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24054  * </ul></p></li>
24055  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24056  * </ul>
24057  * <br>usage:<br><pre><code>
24058 var TopicRecord = Roo.data.Record.create(
24059     {name: 'title', mapping: 'topic_title'},
24060     {name: 'author', mapping: 'username'},
24061     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24062     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24063     {name: 'lastPoster', mapping: 'user2'},
24064     {name: 'excerpt', mapping: 'post_text'}
24065 );
24066
24067 var myNewRecord = new TopicRecord({
24068     title: 'Do my job please',
24069     author: 'noobie',
24070     totalPosts: 1,
24071     lastPost: new Date(),
24072     lastPoster: 'Animal',
24073     excerpt: 'No way dude!'
24074 });
24075 myStore.add(myNewRecord);
24076 </code></pre>
24077  * @method create
24078  * @static
24079  */
24080 Roo.data.Record.create = function(o){
24081     var f = function(){
24082         f.superclass.constructor.apply(this, arguments);
24083     };
24084     Roo.extend(f, Roo.data.Record);
24085     var p = f.prototype;
24086     p.fields = new Roo.util.MixedCollection(false, function(field){
24087         return field.name;
24088     });
24089     for(var i = 0, len = o.length; i < len; i++){
24090         p.fields.add(new Roo.data.Field(o[i]));
24091     }
24092     f.getField = function(name){
24093         return p.fields.get(name);  
24094     };
24095     return f;
24096 };
24097
24098 Roo.data.Record.AUTO_ID = 1000;
24099 Roo.data.Record.EDIT = 'edit';
24100 Roo.data.Record.REJECT = 'reject';
24101 Roo.data.Record.COMMIT = 'commit';
24102
24103 Roo.data.Record.prototype = {
24104     /**
24105      * Readonly flag - true if this record has been modified.
24106      * @type Boolean
24107      */
24108     dirty : false,
24109     editing : false,
24110     error: null,
24111     modified: null,
24112
24113     // private
24114     join : function(store){
24115         this.store = store;
24116     },
24117
24118     /**
24119      * Set the named field to the specified value.
24120      * @param {String} name The name of the field to set.
24121      * @param {Object} value The value to set the field to.
24122      */
24123     set : function(name, value){
24124         if(this.data[name] == value){
24125             return;
24126         }
24127         this.dirty = true;
24128         if(!this.modified){
24129             this.modified = {};
24130         }
24131         if(typeof this.modified[name] == 'undefined'){
24132             this.modified[name] = this.data[name];
24133         }
24134         this.data[name] = value;
24135         if(!this.editing && this.store){
24136             this.store.afterEdit(this);
24137         }       
24138     },
24139
24140     /**
24141      * Get the value of the named field.
24142      * @param {String} name The name of the field to get the value of.
24143      * @return {Object} The value of the field.
24144      */
24145     get : function(name){
24146         return this.data[name]; 
24147     },
24148
24149     // private
24150     beginEdit : function(){
24151         this.editing = true;
24152         this.modified = {}; 
24153     },
24154
24155     // private
24156     cancelEdit : function(){
24157         this.editing = false;
24158         delete this.modified;
24159     },
24160
24161     // private
24162     endEdit : function(){
24163         this.editing = false;
24164         if(this.dirty && this.store){
24165             this.store.afterEdit(this);
24166         }
24167     },
24168
24169     /**
24170      * Usually called by the {@link Roo.data.Store} which owns the Record.
24171      * Rejects all changes made to the Record since either creation, or the last commit operation.
24172      * Modified fields are reverted to their original values.
24173      * <p>
24174      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24175      * of reject operations.
24176      */
24177     reject : function(){
24178         var m = this.modified;
24179         for(var n in m){
24180             if(typeof m[n] != "function"){
24181                 this.data[n] = m[n];
24182             }
24183         }
24184         this.dirty = false;
24185         delete this.modified;
24186         this.editing = false;
24187         if(this.store){
24188             this.store.afterReject(this);
24189         }
24190     },
24191
24192     /**
24193      * Usually called by the {@link Roo.data.Store} which owns the Record.
24194      * Commits all changes made to the Record since either creation, or the last commit operation.
24195      * <p>
24196      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24197      * of commit operations.
24198      */
24199     commit : function(){
24200         this.dirty = false;
24201         delete this.modified;
24202         this.editing = false;
24203         if(this.store){
24204             this.store.afterCommit(this);
24205         }
24206     },
24207
24208     // private
24209     hasError : function(){
24210         return this.error != null;
24211     },
24212
24213     // private
24214     clearError : function(){
24215         this.error = null;
24216     },
24217
24218     /**
24219      * Creates a copy of this record.
24220      * @param {String} id (optional) A new record id if you don't want to use this record's id
24221      * @return {Record}
24222      */
24223     copy : function(newId) {
24224         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24225     }
24226 };/*
24227  * Based on:
24228  * Ext JS Library 1.1.1
24229  * Copyright(c) 2006-2007, Ext JS, LLC.
24230  *
24231  * Originally Released Under LGPL - original licence link has changed is not relivant.
24232  *
24233  * Fork - LGPL
24234  * <script type="text/javascript">
24235  */
24236
24237
24238
24239 /**
24240  * @class Roo.data.Store
24241  * @extends Roo.util.Observable
24242  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24243  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24244  * <p>
24245  * 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
24246  * has no knowledge of the format of the data returned by the Proxy.<br>
24247  * <p>
24248  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24249  * instances from the data object. These records are cached and made available through accessor functions.
24250  * @constructor
24251  * Creates a new Store.
24252  * @param {Object} config A config object containing the objects needed for the Store to access data,
24253  * and read the data into Records.
24254  */
24255 Roo.data.Store = function(config){
24256     this.data = new Roo.util.MixedCollection(false);
24257     this.data.getKey = function(o){
24258         return o.id;
24259     };
24260     this.baseParams = {};
24261     // private
24262     this.paramNames = {
24263         "start" : "start",
24264         "limit" : "limit",
24265         "sort" : "sort",
24266         "dir" : "dir",
24267         "multisort" : "_multisort"
24268     };
24269
24270     if(config && config.data){
24271         this.inlineData = config.data;
24272         delete config.data;
24273     }
24274
24275     Roo.apply(this, config);
24276     
24277     if(this.reader){ // reader passed
24278         this.reader = Roo.factory(this.reader, Roo.data);
24279         this.reader.xmodule = this.xmodule || false;
24280         if(!this.recordType){
24281             this.recordType = this.reader.recordType;
24282         }
24283         if(this.reader.onMetaChange){
24284             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24285         }
24286     }
24287
24288     if(this.recordType){
24289         this.fields = this.recordType.prototype.fields;
24290     }
24291     this.modified = [];
24292
24293     this.addEvents({
24294         /**
24295          * @event datachanged
24296          * Fires when the data cache has changed, and a widget which is using this Store
24297          * as a Record cache should refresh its view.
24298          * @param {Store} this
24299          */
24300         datachanged : true,
24301         /**
24302          * @event metachange
24303          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24304          * @param {Store} this
24305          * @param {Object} meta The JSON metadata
24306          */
24307         metachange : true,
24308         /**
24309          * @event add
24310          * Fires when Records have been added to the Store
24311          * @param {Store} this
24312          * @param {Roo.data.Record[]} records The array of Records added
24313          * @param {Number} index The index at which the record(s) were added
24314          */
24315         add : true,
24316         /**
24317          * @event remove
24318          * Fires when a Record has been removed from the Store
24319          * @param {Store} this
24320          * @param {Roo.data.Record} record The Record that was removed
24321          * @param {Number} index The index at which the record was removed
24322          */
24323         remove : true,
24324         /**
24325          * @event update
24326          * Fires when a Record has been updated
24327          * @param {Store} this
24328          * @param {Roo.data.Record} record The Record that was updated
24329          * @param {String} operation The update operation being performed.  Value may be one of:
24330          * <pre><code>
24331  Roo.data.Record.EDIT
24332  Roo.data.Record.REJECT
24333  Roo.data.Record.COMMIT
24334          * </code></pre>
24335          */
24336         update : true,
24337         /**
24338          * @event clear
24339          * Fires when the data cache has been cleared.
24340          * @param {Store} this
24341          */
24342         clear : true,
24343         /**
24344          * @event beforeload
24345          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24346          * the load action will be canceled.
24347          * @param {Store} this
24348          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24349          */
24350         beforeload : true,
24351         /**
24352          * @event beforeloadadd
24353          * Fires after a new set of Records has been loaded.
24354          * @param {Store} this
24355          * @param {Roo.data.Record[]} records The Records that were loaded
24356          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24357          */
24358         beforeloadadd : true,
24359         /**
24360          * @event load
24361          * Fires after a new set of Records has been loaded, before they are added to the store.
24362          * @param {Store} this
24363          * @param {Roo.data.Record[]} records The Records that were loaded
24364          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24365          * @params {Object} return from reader
24366          */
24367         load : true,
24368         /**
24369          * @event loadexception
24370          * Fires if an exception occurs in the Proxy during loading.
24371          * Called with the signature of the Proxy's "loadexception" event.
24372          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24373          * 
24374          * @param {Proxy} 
24375          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24376          * @param {Object} load options 
24377          * @param {Object} jsonData from your request (normally this contains the Exception)
24378          */
24379         loadexception : true
24380     });
24381     
24382     if(this.proxy){
24383         this.proxy = Roo.factory(this.proxy, Roo.data);
24384         this.proxy.xmodule = this.xmodule || false;
24385         this.relayEvents(this.proxy,  ["loadexception"]);
24386     }
24387     this.sortToggle = {};
24388     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24389
24390     Roo.data.Store.superclass.constructor.call(this);
24391
24392     if(this.inlineData){
24393         this.loadData(this.inlineData);
24394         delete this.inlineData;
24395     }
24396 };
24397
24398 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24399      /**
24400     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24401     * without a remote query - used by combo/forms at present.
24402     */
24403     
24404     /**
24405     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24406     */
24407     /**
24408     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24409     */
24410     /**
24411     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24412     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24413     */
24414     /**
24415     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24416     * on any HTTP request
24417     */
24418     /**
24419     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24420     */
24421     /**
24422     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24423     */
24424     multiSort: false,
24425     /**
24426     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24427     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24428     */
24429     remoteSort : false,
24430
24431     /**
24432     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24433      * loaded or when a record is removed. (defaults to false).
24434     */
24435     pruneModifiedRecords : false,
24436
24437     // private
24438     lastOptions : null,
24439
24440     /**
24441      * Add Records to the Store and fires the add event.
24442      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24443      */
24444     add : function(records){
24445         records = [].concat(records);
24446         for(var i = 0, len = records.length; i < len; i++){
24447             records[i].join(this);
24448         }
24449         var index = this.data.length;
24450         this.data.addAll(records);
24451         this.fireEvent("add", this, records, index);
24452     },
24453
24454     /**
24455      * Remove a Record from the Store and fires the remove event.
24456      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24457      */
24458     remove : function(record){
24459         var index = this.data.indexOf(record);
24460         this.data.removeAt(index);
24461  
24462         if(this.pruneModifiedRecords){
24463             this.modified.remove(record);
24464         }
24465         this.fireEvent("remove", this, record, index);
24466     },
24467
24468     /**
24469      * Remove all Records from the Store and fires the clear event.
24470      */
24471     removeAll : function(){
24472         this.data.clear();
24473         if(this.pruneModifiedRecords){
24474             this.modified = [];
24475         }
24476         this.fireEvent("clear", this);
24477     },
24478
24479     /**
24480      * Inserts Records to the Store at the given index and fires the add event.
24481      * @param {Number} index The start index at which to insert the passed Records.
24482      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24483      */
24484     insert : function(index, records){
24485         records = [].concat(records);
24486         for(var i = 0, len = records.length; i < len; i++){
24487             this.data.insert(index, records[i]);
24488             records[i].join(this);
24489         }
24490         this.fireEvent("add", this, records, index);
24491     },
24492
24493     /**
24494      * Get the index within the cache of the passed Record.
24495      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24496      * @return {Number} The index of the passed Record. Returns -1 if not found.
24497      */
24498     indexOf : function(record){
24499         return this.data.indexOf(record);
24500     },
24501
24502     /**
24503      * Get the index within the cache of the Record with the passed id.
24504      * @param {String} id The id of the Record to find.
24505      * @return {Number} The index of the Record. Returns -1 if not found.
24506      */
24507     indexOfId : function(id){
24508         return this.data.indexOfKey(id);
24509     },
24510
24511     /**
24512      * Get the Record with the specified id.
24513      * @param {String} id The id of the Record to find.
24514      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24515      */
24516     getById : function(id){
24517         return this.data.key(id);
24518     },
24519
24520     /**
24521      * Get the Record at the specified index.
24522      * @param {Number} index The index of the Record to find.
24523      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24524      */
24525     getAt : function(index){
24526         return this.data.itemAt(index);
24527     },
24528
24529     /**
24530      * Returns a range of Records between specified indices.
24531      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24532      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24533      * @return {Roo.data.Record[]} An array of Records
24534      */
24535     getRange : function(start, end){
24536         return this.data.getRange(start, end);
24537     },
24538
24539     // private
24540     storeOptions : function(o){
24541         o = Roo.apply({}, o);
24542         delete o.callback;
24543         delete o.scope;
24544         this.lastOptions = o;
24545     },
24546
24547     /**
24548      * Loads the Record cache from the configured Proxy using the configured Reader.
24549      * <p>
24550      * If using remote paging, then the first load call must specify the <em>start</em>
24551      * and <em>limit</em> properties in the options.params property to establish the initial
24552      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24553      * <p>
24554      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24555      * and this call will return before the new data has been loaded. Perform any post-processing
24556      * in a callback function, or in a "load" event handler.</strong>
24557      * <p>
24558      * @param {Object} options An object containing properties which control loading options:<ul>
24559      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24560      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24561      * passed the following arguments:<ul>
24562      * <li>r : Roo.data.Record[]</li>
24563      * <li>options: Options object from the load call</li>
24564      * <li>success: Boolean success indicator</li></ul></li>
24565      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24566      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24567      * </ul>
24568      */
24569     load : function(options){
24570         options = options || {};
24571         if(this.fireEvent("beforeload", this, options) !== false){
24572             this.storeOptions(options);
24573             var p = Roo.apply(options.params || {}, this.baseParams);
24574             // if meta was not loaded from remote source.. try requesting it.
24575             if (!this.reader.metaFromRemote) {
24576                 p._requestMeta = 1;
24577             }
24578             if(this.sortInfo && this.remoteSort){
24579                 var pn = this.paramNames;
24580                 p[pn["sort"]] = this.sortInfo.field;
24581                 p[pn["dir"]] = this.sortInfo.direction;
24582             }
24583             if (this.multiSort) {
24584                 var pn = this.paramNames;
24585                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24586             }
24587             
24588             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24589         }
24590     },
24591
24592     /**
24593      * Reloads the Record cache from the configured Proxy using the configured Reader and
24594      * the options from the last load operation performed.
24595      * @param {Object} options (optional) An object containing properties which may override the options
24596      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24597      * the most recently used options are reused).
24598      */
24599     reload : function(options){
24600         this.load(Roo.applyIf(options||{}, this.lastOptions));
24601     },
24602
24603     // private
24604     // Called as a callback by the Reader during a load operation.
24605     loadRecords : function(o, options, success){
24606          
24607         if(!o){
24608             if(success !== false){
24609                 this.fireEvent("load", this, [], options, o);
24610             }
24611             if(options.callback){
24612                 options.callback.call(options.scope || this, [], options, false);
24613             }
24614             return;
24615         }
24616         // if data returned failure - throw an exception.
24617         if (o.success === false) {
24618             // show a message if no listener is registered.
24619             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24620                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24621             }
24622             // loadmask wil be hooked into this..
24623             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24624             return;
24625         }
24626         var r = o.records, t = o.totalRecords || r.length;
24627         
24628         this.fireEvent("beforeloadadd", this, r, options, o);
24629         
24630         if(!options || options.add !== true){
24631             if(this.pruneModifiedRecords){
24632                 this.modified = [];
24633             }
24634             for(var i = 0, len = r.length; i < len; i++){
24635                 r[i].join(this);
24636             }
24637             if(this.snapshot){
24638                 this.data = this.snapshot;
24639                 delete this.snapshot;
24640             }
24641             this.data.clear();
24642             this.data.addAll(r);
24643             this.totalLength = t;
24644             this.applySort();
24645             this.fireEvent("datachanged", this);
24646         }else{
24647             this.totalLength = Math.max(t, this.data.length+r.length);
24648             this.add(r);
24649         }
24650         
24651         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24652                 
24653             var e = new Roo.data.Record({});
24654
24655             e.set(this.parent.displayField, this.parent.emptyTitle);
24656             e.set(this.parent.valueField, '');
24657
24658             this.insert(0, e);
24659         }
24660             
24661         this.fireEvent("load", this, r, options, o);
24662         if(options.callback){
24663             options.callback.call(options.scope || this, r, options, true);
24664         }
24665     },
24666
24667
24668     /**
24669      * Loads data from a passed data block. A Reader which understands the format of the data
24670      * must have been configured in the constructor.
24671      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24672      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24673      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24674      */
24675     loadData : function(o, append){
24676         var r = this.reader.readRecords(o);
24677         this.loadRecords(r, {add: append}, true);
24678     },
24679     
24680      /**
24681      * using 'cn' the nested child reader read the child array into it's child stores.
24682      * @param {Object} rec The record with a 'children array
24683      */
24684     loadDataFromChildren : function(rec)
24685     {
24686         this.loadData(this.reader.toLoadData(rec));
24687     },
24688     
24689
24690     /**
24691      * Gets the number of cached records.
24692      * <p>
24693      * <em>If using paging, this may not be the total size of the dataset. If the data object
24694      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24695      * the data set size</em>
24696      */
24697     getCount : function(){
24698         return this.data.length || 0;
24699     },
24700
24701     /**
24702      * Gets the total number of records in the dataset as returned by the server.
24703      * <p>
24704      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24705      * the dataset size</em>
24706      */
24707     getTotalCount : function(){
24708         return this.totalLength || 0;
24709     },
24710
24711     /**
24712      * Returns the sort state of the Store as an object with two properties:
24713      * <pre><code>
24714  field {String} The name of the field by which the Records are sorted
24715  direction {String} The sort order, "ASC" or "DESC"
24716      * </code></pre>
24717      */
24718     getSortState : function(){
24719         return this.sortInfo;
24720     },
24721
24722     // private
24723     applySort : function(){
24724         if(this.sortInfo && !this.remoteSort){
24725             var s = this.sortInfo, f = s.field;
24726             var st = this.fields.get(f).sortType;
24727             var fn = function(r1, r2){
24728                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24729                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24730             };
24731             this.data.sort(s.direction, fn);
24732             if(this.snapshot && this.snapshot != this.data){
24733                 this.snapshot.sort(s.direction, fn);
24734             }
24735         }
24736     },
24737
24738     /**
24739      * Sets the default sort column and order to be used by the next load operation.
24740      * @param {String} fieldName The name of the field to sort by.
24741      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24742      */
24743     setDefaultSort : function(field, dir){
24744         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24745     },
24746
24747     /**
24748      * Sort the Records.
24749      * If remote sorting is used, the sort is performed on the server, and the cache is
24750      * reloaded. If local sorting is used, the cache is sorted internally.
24751      * @param {String} fieldName The name of the field to sort by.
24752      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24753      */
24754     sort : function(fieldName, dir){
24755         var f = this.fields.get(fieldName);
24756         if(!dir){
24757             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24758             
24759             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24760                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24761             }else{
24762                 dir = f.sortDir;
24763             }
24764         }
24765         this.sortToggle[f.name] = dir;
24766         this.sortInfo = {field: f.name, direction: dir};
24767         if(!this.remoteSort){
24768             this.applySort();
24769             this.fireEvent("datachanged", this);
24770         }else{
24771             this.load(this.lastOptions);
24772         }
24773     },
24774
24775     /**
24776      * Calls the specified function for each of the Records in the cache.
24777      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24778      * Returning <em>false</em> aborts and exits the iteration.
24779      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24780      */
24781     each : function(fn, scope){
24782         this.data.each(fn, scope);
24783     },
24784
24785     /**
24786      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24787      * (e.g., during paging).
24788      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24789      */
24790     getModifiedRecords : function(){
24791         return this.modified;
24792     },
24793
24794     // private
24795     createFilterFn : function(property, value, anyMatch){
24796         if(!value.exec){ // not a regex
24797             value = String(value);
24798             if(value.length == 0){
24799                 return false;
24800             }
24801             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24802         }
24803         return function(r){
24804             return value.test(r.data[property]);
24805         };
24806     },
24807
24808     /**
24809      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24810      * @param {String} property A field on your records
24811      * @param {Number} start The record index to start at (defaults to 0)
24812      * @param {Number} end The last record index to include (defaults to length - 1)
24813      * @return {Number} The sum
24814      */
24815     sum : function(property, start, end){
24816         var rs = this.data.items, v = 0;
24817         start = start || 0;
24818         end = (end || end === 0) ? end : rs.length-1;
24819
24820         for(var i = start; i <= end; i++){
24821             v += (rs[i].data[property] || 0);
24822         }
24823         return v;
24824     },
24825
24826     /**
24827      * Filter the records by a specified property.
24828      * @param {String} field A field on your records
24829      * @param {String/RegExp} value Either a string that the field
24830      * should start with or a RegExp to test against the field
24831      * @param {Boolean} anyMatch True to match any part not just the beginning
24832      */
24833     filter : function(property, value, anyMatch){
24834         var fn = this.createFilterFn(property, value, anyMatch);
24835         return fn ? this.filterBy(fn) : this.clearFilter();
24836     },
24837
24838     /**
24839      * Filter by a function. The specified function will be called with each
24840      * record in this data source. If the function returns true the record is included,
24841      * otherwise it is filtered.
24842      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24843      * @param {Object} scope (optional) The scope of the function (defaults to this)
24844      */
24845     filterBy : function(fn, scope){
24846         this.snapshot = this.snapshot || this.data;
24847         this.data = this.queryBy(fn, scope||this);
24848         this.fireEvent("datachanged", this);
24849     },
24850
24851     /**
24852      * Query the records by a specified property.
24853      * @param {String} field A field on your records
24854      * @param {String/RegExp} value Either a string that the field
24855      * should start with or a RegExp to test against the field
24856      * @param {Boolean} anyMatch True to match any part not just the beginning
24857      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24858      */
24859     query : function(property, value, anyMatch){
24860         var fn = this.createFilterFn(property, value, anyMatch);
24861         return fn ? this.queryBy(fn) : this.data.clone();
24862     },
24863
24864     /**
24865      * Query by a function. The specified function will be called with each
24866      * record in this data source. If the function returns true the record is included
24867      * in the results.
24868      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24869      * @param {Object} scope (optional) The scope of the function (defaults to this)
24870       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24871      **/
24872     queryBy : function(fn, scope){
24873         var data = this.snapshot || this.data;
24874         return data.filterBy(fn, scope||this);
24875     },
24876
24877     /**
24878      * Collects unique values for a particular dataIndex from this store.
24879      * @param {String} dataIndex The property to collect
24880      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24881      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24882      * @return {Array} An array of the unique values
24883      **/
24884     collect : function(dataIndex, allowNull, bypassFilter){
24885         var d = (bypassFilter === true && this.snapshot) ?
24886                 this.snapshot.items : this.data.items;
24887         var v, sv, r = [], l = {};
24888         for(var i = 0, len = d.length; i < len; i++){
24889             v = d[i].data[dataIndex];
24890             sv = String(v);
24891             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24892                 l[sv] = true;
24893                 r[r.length] = v;
24894             }
24895         }
24896         return r;
24897     },
24898
24899     /**
24900      * Revert to a view of the Record cache with no filtering applied.
24901      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24902      */
24903     clearFilter : function(suppressEvent){
24904         if(this.snapshot && this.snapshot != this.data){
24905             this.data = this.snapshot;
24906             delete this.snapshot;
24907             if(suppressEvent !== true){
24908                 this.fireEvent("datachanged", this);
24909             }
24910         }
24911     },
24912
24913     // private
24914     afterEdit : function(record){
24915         if(this.modified.indexOf(record) == -1){
24916             this.modified.push(record);
24917         }
24918         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24919     },
24920     
24921     // private
24922     afterReject : function(record){
24923         this.modified.remove(record);
24924         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24925     },
24926
24927     // private
24928     afterCommit : function(record){
24929         this.modified.remove(record);
24930         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24931     },
24932
24933     /**
24934      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24935      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24936      */
24937     commitChanges : function(){
24938         var m = this.modified.slice(0);
24939         this.modified = [];
24940         for(var i = 0, len = m.length; i < len; i++){
24941             m[i].commit();
24942         }
24943     },
24944
24945     /**
24946      * Cancel outstanding changes on all changed records.
24947      */
24948     rejectChanges : function(){
24949         var m = this.modified.slice(0);
24950         this.modified = [];
24951         for(var i = 0, len = m.length; i < len; i++){
24952             m[i].reject();
24953         }
24954     },
24955
24956     onMetaChange : function(meta, rtype, o){
24957         this.recordType = rtype;
24958         this.fields = rtype.prototype.fields;
24959         delete this.snapshot;
24960         this.sortInfo = meta.sortInfo || this.sortInfo;
24961         this.modified = [];
24962         this.fireEvent('metachange', this, this.reader.meta);
24963     },
24964     
24965     moveIndex : function(data, type)
24966     {
24967         var index = this.indexOf(data);
24968         
24969         var newIndex = index + type;
24970         
24971         this.remove(data);
24972         
24973         this.insert(newIndex, data);
24974         
24975     }
24976 });/*
24977  * Based on:
24978  * Ext JS Library 1.1.1
24979  * Copyright(c) 2006-2007, Ext JS, LLC.
24980  *
24981  * Originally Released Under LGPL - original licence link has changed is not relivant.
24982  *
24983  * Fork - LGPL
24984  * <script type="text/javascript">
24985  */
24986
24987 /**
24988  * @class Roo.data.SimpleStore
24989  * @extends Roo.data.Store
24990  * Small helper class to make creating Stores from Array data easier.
24991  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24992  * @cfg {Array} fields An array of field definition objects, or field name strings.
24993  * @cfg {Object} an existing reader (eg. copied from another store)
24994  * @cfg {Array} data The multi-dimensional array of data
24995  * @cfg {Roo.data.DataProxy} proxy [not-required]  
24996  * @cfg {Roo.data.Reader} reader  [not-required] 
24997  * @constructor
24998  * @param {Object} config
24999  */
25000 Roo.data.SimpleStore = function(config)
25001 {
25002     Roo.data.SimpleStore.superclass.constructor.call(this, {
25003         isLocal : true,
25004         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25005                 id: config.id
25006             },
25007             Roo.data.Record.create(config.fields)
25008         ),
25009         proxy : new Roo.data.MemoryProxy(config.data)
25010     });
25011     this.load();
25012 };
25013 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25014  * Based on:
25015  * Ext JS Library 1.1.1
25016  * Copyright(c) 2006-2007, Ext JS, LLC.
25017  *
25018  * Originally Released Under LGPL - original licence link has changed is not relivant.
25019  *
25020  * Fork - LGPL
25021  * <script type="text/javascript">
25022  */
25023
25024 /**
25025 /**
25026  * @extends Roo.data.Store
25027  * @class Roo.data.JsonStore
25028  * Small helper class to make creating Stores for JSON data easier. <br/>
25029 <pre><code>
25030 var store = new Roo.data.JsonStore({
25031     url: 'get-images.php',
25032     root: 'images',
25033     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25034 });
25035 </code></pre>
25036  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25037  * JsonReader and HttpProxy (unless inline data is provided).</b>
25038  * @cfg {Array} fields An array of field definition objects, or field name strings.
25039  * @constructor
25040  * @param {Object} config
25041  */
25042 Roo.data.JsonStore = function(c){
25043     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25044         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25045         reader: new Roo.data.JsonReader(c, c.fields)
25046     }));
25047 };
25048 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25049  * Based on:
25050  * Ext JS Library 1.1.1
25051  * Copyright(c) 2006-2007, Ext JS, LLC.
25052  *
25053  * Originally Released Under LGPL - original licence link has changed is not relivant.
25054  *
25055  * Fork - LGPL
25056  * <script type="text/javascript">
25057  */
25058
25059  
25060 Roo.data.Field = function(config){
25061     if(typeof config == "string"){
25062         config = {name: config};
25063     }
25064     Roo.apply(this, config);
25065     
25066     if(!this.type){
25067         this.type = "auto";
25068     }
25069     
25070     var st = Roo.data.SortTypes;
25071     // named sortTypes are supported, here we look them up
25072     if(typeof this.sortType == "string"){
25073         this.sortType = st[this.sortType];
25074     }
25075     
25076     // set default sortType for strings and dates
25077     if(!this.sortType){
25078         switch(this.type){
25079             case "string":
25080                 this.sortType = st.asUCString;
25081                 break;
25082             case "date":
25083                 this.sortType = st.asDate;
25084                 break;
25085             default:
25086                 this.sortType = st.none;
25087         }
25088     }
25089
25090     // define once
25091     var stripRe = /[\$,%]/g;
25092
25093     // prebuilt conversion function for this field, instead of
25094     // switching every time we're reading a value
25095     if(!this.convert){
25096         var cv, dateFormat = this.dateFormat;
25097         switch(this.type){
25098             case "":
25099             case "auto":
25100             case undefined:
25101                 cv = function(v){ return v; };
25102                 break;
25103             case "string":
25104                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25105                 break;
25106             case "int":
25107                 cv = function(v){
25108                     return v !== undefined && v !== null && v !== '' ?
25109                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25110                     };
25111                 break;
25112             case "float":
25113                 cv = function(v){
25114                     return v !== undefined && v !== null && v !== '' ?
25115                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25116                     };
25117                 break;
25118             case "bool":
25119             case "boolean":
25120                 cv = function(v){ return v === true || v === "true" || v == 1; };
25121                 break;
25122             case "date":
25123                 cv = function(v){
25124                     if(!v){
25125                         return '';
25126                     }
25127                     if(v instanceof Date){
25128                         return v;
25129                     }
25130                     if(dateFormat){
25131                         if(dateFormat == "timestamp"){
25132                             return new Date(v*1000);
25133                         }
25134                         return Date.parseDate(v, dateFormat);
25135                     }
25136                     var parsed = Date.parse(v);
25137                     return parsed ? new Date(parsed) : null;
25138                 };
25139              break;
25140             
25141         }
25142         this.convert = cv;
25143     }
25144 };
25145
25146 Roo.data.Field.prototype = {
25147     dateFormat: null,
25148     defaultValue: "",
25149     mapping: null,
25150     sortType : null,
25151     sortDir : "ASC"
25152 };/*
25153  * Based on:
25154  * Ext JS Library 1.1.1
25155  * Copyright(c) 2006-2007, Ext JS, LLC.
25156  *
25157  * Originally Released Under LGPL - original licence link has changed is not relivant.
25158  *
25159  * Fork - LGPL
25160  * <script type="text/javascript">
25161  */
25162  
25163 // Base class for reading structured data from a data source.  This class is intended to be
25164 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25165
25166 /**
25167  * @class Roo.data.DataReader
25168  * @abstract
25169  * Base class for reading structured data from a data source.  This class is intended to be
25170  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25171  */
25172
25173 Roo.data.DataReader = function(meta, recordType){
25174     
25175     this.meta = meta;
25176     
25177     this.recordType = recordType instanceof Array ? 
25178         Roo.data.Record.create(recordType) : recordType;
25179 };
25180
25181 Roo.data.DataReader.prototype = {
25182     
25183     
25184     readerType : 'Data',
25185      /**
25186      * Create an empty record
25187      * @param {Object} data (optional) - overlay some values
25188      * @return {Roo.data.Record} record created.
25189      */
25190     newRow :  function(d) {
25191         var da =  {};
25192         this.recordType.prototype.fields.each(function(c) {
25193             switch( c.type) {
25194                 case 'int' : da[c.name] = 0; break;
25195                 case 'date' : da[c.name] = new Date(); break;
25196                 case 'float' : da[c.name] = 0.0; break;
25197                 case 'boolean' : da[c.name] = false; break;
25198                 default : da[c.name] = ""; break;
25199             }
25200             
25201         });
25202         return new this.recordType(Roo.apply(da, d));
25203     }
25204     
25205     
25206 };/*
25207  * Based on:
25208  * Ext JS Library 1.1.1
25209  * Copyright(c) 2006-2007, Ext JS, LLC.
25210  *
25211  * Originally Released Under LGPL - original licence link has changed is not relivant.
25212  *
25213  * Fork - LGPL
25214  * <script type="text/javascript">
25215  */
25216
25217 /**
25218  * @class Roo.data.DataProxy
25219  * @extends Roo.util.Observable
25220  * @abstract
25221  * This class is an abstract base class for implementations which provide retrieval of
25222  * unformatted data objects.<br>
25223  * <p>
25224  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25225  * (of the appropriate type which knows how to parse the data object) to provide a block of
25226  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25227  * <p>
25228  * Custom implementations must implement the load method as described in
25229  * {@link Roo.data.HttpProxy#load}.
25230  */
25231 Roo.data.DataProxy = function(){
25232     this.addEvents({
25233         /**
25234          * @event beforeload
25235          * Fires before a network request is made to retrieve a data object.
25236          * @param {Object} This DataProxy object.
25237          * @param {Object} params The params parameter to the load function.
25238          */
25239         beforeload : true,
25240         /**
25241          * @event load
25242          * Fires before the load method's callback is called.
25243          * @param {Object} This DataProxy object.
25244          * @param {Object} o The data object.
25245          * @param {Object} arg The callback argument object passed to the load function.
25246          */
25247         load : true,
25248         /**
25249          * @event loadexception
25250          * Fires if an Exception occurs during data retrieval.
25251          * @param {Object} This DataProxy object.
25252          * @param {Object} o The data object.
25253          * @param {Object} arg The callback argument object passed to the load function.
25254          * @param {Object} e The Exception.
25255          */
25256         loadexception : true
25257     });
25258     Roo.data.DataProxy.superclass.constructor.call(this);
25259 };
25260
25261 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25262
25263     /**
25264      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25265      */
25266 /*
25267  * Based on:
25268  * Ext JS Library 1.1.1
25269  * Copyright(c) 2006-2007, Ext JS, LLC.
25270  *
25271  * Originally Released Under LGPL - original licence link has changed is not relivant.
25272  *
25273  * Fork - LGPL
25274  * <script type="text/javascript">
25275  */
25276 /**
25277  * @class Roo.data.MemoryProxy
25278  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25279  * to the Reader when its load method is called.
25280  * @constructor
25281  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25282  */
25283 Roo.data.MemoryProxy = function(data){
25284     if (data.data) {
25285         data = data.data;
25286     }
25287     Roo.data.MemoryProxy.superclass.constructor.call(this);
25288     this.data = data;
25289 };
25290
25291 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25292     
25293     /**
25294      * Load data from the requested source (in this case an in-memory
25295      * data object passed to the constructor), read the data object into
25296      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25297      * process that block using the passed callback.
25298      * @param {Object} params This parameter is not used by the MemoryProxy class.
25299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25300      * object into a block of Roo.data.Records.
25301      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25302      * The function must be passed <ul>
25303      * <li>The Record block object</li>
25304      * <li>The "arg" argument from the load function</li>
25305      * <li>A boolean success indicator</li>
25306      * </ul>
25307      * @param {Object} scope The scope in which to call the callback
25308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25309      */
25310     load : function(params, reader, callback, scope, arg){
25311         params = params || {};
25312         var result;
25313         try {
25314             result = reader.readRecords(params.data ? params.data :this.data);
25315         }catch(e){
25316             this.fireEvent("loadexception", this, arg, null, e);
25317             callback.call(scope, null, arg, false);
25318             return;
25319         }
25320         callback.call(scope, result, arg, true);
25321     },
25322     
25323     // private
25324     update : function(params, records){
25325         
25326     }
25327 });/*
25328  * Based on:
25329  * Ext JS Library 1.1.1
25330  * Copyright(c) 2006-2007, Ext JS, LLC.
25331  *
25332  * Originally Released Under LGPL - original licence link has changed is not relivant.
25333  *
25334  * Fork - LGPL
25335  * <script type="text/javascript">
25336  */
25337 /**
25338  * @class Roo.data.HttpProxy
25339  * @extends Roo.data.DataProxy
25340  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25341  * configured to reference a certain URL.<br><br>
25342  * <p>
25343  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25344  * from which the running page was served.<br><br>
25345  * <p>
25346  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25347  * <p>
25348  * Be aware that to enable the browser to parse an XML document, the server must set
25349  * the Content-Type header in the HTTP response to "text/xml".
25350  * @constructor
25351  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25352  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25353  * will be used to make the request.
25354  */
25355 Roo.data.HttpProxy = function(conn){
25356     Roo.data.HttpProxy.superclass.constructor.call(this);
25357     // is conn a conn config or a real conn?
25358     this.conn = conn;
25359     this.useAjax = !conn || !conn.events;
25360   
25361 };
25362
25363 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25364     // thse are take from connection...
25365     
25366     /**
25367      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25368      */
25369     /**
25370      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25371      * extra parameters to each request made by this object. (defaults to undefined)
25372      */
25373     /**
25374      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25375      *  to each request made by this object. (defaults to undefined)
25376      */
25377     /**
25378      * @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)
25379      */
25380     /**
25381      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25382      */
25383      /**
25384      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25385      * @type Boolean
25386      */
25387   
25388
25389     /**
25390      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25391      * @type Boolean
25392      */
25393     /**
25394      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25395      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25396      * a finer-grained basis than the DataProxy events.
25397      */
25398     getConnection : function(){
25399         return this.useAjax ? Roo.Ajax : this.conn;
25400     },
25401
25402     /**
25403      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25404      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25405      * process that block using the passed callback.
25406      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25407      * for the request to the remote server.
25408      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25409      * object into a block of Roo.data.Records.
25410      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25411      * The function must be passed <ul>
25412      * <li>The Record block object</li>
25413      * <li>The "arg" argument from the load function</li>
25414      * <li>A boolean success indicator</li>
25415      * </ul>
25416      * @param {Object} scope The scope in which to call the callback
25417      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25418      */
25419     load : function(params, reader, callback, scope, arg){
25420         if(this.fireEvent("beforeload", this, params) !== false){
25421             var  o = {
25422                 params : params || {},
25423                 request: {
25424                     callback : callback,
25425                     scope : scope,
25426                     arg : arg
25427                 },
25428                 reader: reader,
25429                 callback : this.loadResponse,
25430                 scope: this
25431             };
25432             if(this.useAjax){
25433                 Roo.applyIf(o, this.conn);
25434                 if(this.activeRequest){
25435                     Roo.Ajax.abort(this.activeRequest);
25436                 }
25437                 this.activeRequest = Roo.Ajax.request(o);
25438             }else{
25439                 this.conn.request(o);
25440             }
25441         }else{
25442             callback.call(scope||this, null, arg, false);
25443         }
25444     },
25445
25446     // private
25447     loadResponse : function(o, success, response){
25448         delete this.activeRequest;
25449         if(!success){
25450             this.fireEvent("loadexception", this, o, response);
25451             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25452             return;
25453         }
25454         var result;
25455         try {
25456             result = o.reader.read(response);
25457         }catch(e){
25458             o.success = false;
25459             o.raw = { errorMsg : response.responseText };
25460             this.fireEvent("loadexception", this, o, response, e);
25461             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25462             return;
25463         }
25464         
25465         this.fireEvent("load", this, o, o.request.arg);
25466         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25467     },
25468
25469     // private
25470     update : function(dataSet){
25471
25472     },
25473
25474     // private
25475     updateResponse : function(dataSet){
25476
25477     }
25478 });/*
25479  * Based on:
25480  * Ext JS Library 1.1.1
25481  * Copyright(c) 2006-2007, Ext JS, LLC.
25482  *
25483  * Originally Released Under LGPL - original licence link has changed is not relivant.
25484  *
25485  * Fork - LGPL
25486  * <script type="text/javascript">
25487  */
25488
25489 /**
25490  * @class Roo.data.ScriptTagProxy
25491  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25492  * other than the originating domain of the running page.<br><br>
25493  * <p>
25494  * <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
25495  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25496  * <p>
25497  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25498  * source code that is used as the source inside a &lt;script> tag.<br><br>
25499  * <p>
25500  * In order for the browser to process the returned data, the server must wrap the data object
25501  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25502  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25503  * depending on whether the callback name was passed:
25504  * <p>
25505  * <pre><code>
25506 boolean scriptTag = false;
25507 String cb = request.getParameter("callback");
25508 if (cb != null) {
25509     scriptTag = true;
25510     response.setContentType("text/javascript");
25511 } else {
25512     response.setContentType("application/x-json");
25513 }
25514 Writer out = response.getWriter();
25515 if (scriptTag) {
25516     out.write(cb + "(");
25517 }
25518 out.print(dataBlock.toJsonString());
25519 if (scriptTag) {
25520     out.write(");");
25521 }
25522 </pre></code>
25523  *
25524  * @constructor
25525  * @param {Object} config A configuration object.
25526  */
25527 Roo.data.ScriptTagProxy = function(config){
25528     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25529     Roo.apply(this, config);
25530     this.head = document.getElementsByTagName("head")[0];
25531 };
25532
25533 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25534
25535 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25536     /**
25537      * @cfg {String} url The URL from which to request the data object.
25538      */
25539     /**
25540      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25541      */
25542     timeout : 30000,
25543     /**
25544      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25545      * the server the name of the callback function set up by the load call to process the returned data object.
25546      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25547      * javascript output which calls this named function passing the data object as its only parameter.
25548      */
25549     callbackParam : "callback",
25550     /**
25551      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25552      * name to the request.
25553      */
25554     nocache : true,
25555
25556     /**
25557      * Load data from the configured URL, read the data object into
25558      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25559      * process that block using the passed callback.
25560      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25561      * for the request to the remote server.
25562      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25563      * object into a block of Roo.data.Records.
25564      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25565      * The function must be passed <ul>
25566      * <li>The Record block object</li>
25567      * <li>The "arg" argument from the load function</li>
25568      * <li>A boolean success indicator</li>
25569      * </ul>
25570      * @param {Object} scope The scope in which to call the callback
25571      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25572      */
25573     load : function(params, reader, callback, scope, arg){
25574         if(this.fireEvent("beforeload", this, params) !== false){
25575
25576             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25577
25578             var url = this.url;
25579             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25580             if(this.nocache){
25581                 url += "&_dc=" + (new Date().getTime());
25582             }
25583             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25584             var trans = {
25585                 id : transId,
25586                 cb : "stcCallback"+transId,
25587                 scriptId : "stcScript"+transId,
25588                 params : params,
25589                 arg : arg,
25590                 url : url,
25591                 callback : callback,
25592                 scope : scope,
25593                 reader : reader
25594             };
25595             var conn = this;
25596
25597             window[trans.cb] = function(o){
25598                 conn.handleResponse(o, trans);
25599             };
25600
25601             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25602
25603             if(this.autoAbort !== false){
25604                 this.abort();
25605             }
25606
25607             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25608
25609             var script = document.createElement("script");
25610             script.setAttribute("src", url);
25611             script.setAttribute("type", "text/javascript");
25612             script.setAttribute("id", trans.scriptId);
25613             this.head.appendChild(script);
25614
25615             this.trans = trans;
25616         }else{
25617             callback.call(scope||this, null, arg, false);
25618         }
25619     },
25620
25621     // private
25622     isLoading : function(){
25623         return this.trans ? true : false;
25624     },
25625
25626     /**
25627      * Abort the current server request.
25628      */
25629     abort : function(){
25630         if(this.isLoading()){
25631             this.destroyTrans(this.trans);
25632         }
25633     },
25634
25635     // private
25636     destroyTrans : function(trans, isLoaded){
25637         this.head.removeChild(document.getElementById(trans.scriptId));
25638         clearTimeout(trans.timeoutId);
25639         if(isLoaded){
25640             window[trans.cb] = undefined;
25641             try{
25642                 delete window[trans.cb];
25643             }catch(e){}
25644         }else{
25645             // if hasn't been loaded, wait for load to remove it to prevent script error
25646             window[trans.cb] = function(){
25647                 window[trans.cb] = undefined;
25648                 try{
25649                     delete window[trans.cb];
25650                 }catch(e){}
25651             };
25652         }
25653     },
25654
25655     // private
25656     handleResponse : function(o, trans){
25657         this.trans = false;
25658         this.destroyTrans(trans, true);
25659         var result;
25660         try {
25661             result = trans.reader.readRecords(o);
25662         }catch(e){
25663             this.fireEvent("loadexception", this, o, trans.arg, e);
25664             trans.callback.call(trans.scope||window, null, trans.arg, false);
25665             return;
25666         }
25667         this.fireEvent("load", this, o, trans.arg);
25668         trans.callback.call(trans.scope||window, result, trans.arg, true);
25669     },
25670
25671     // private
25672     handleFailure : function(trans){
25673         this.trans = false;
25674         this.destroyTrans(trans, false);
25675         this.fireEvent("loadexception", this, null, trans.arg);
25676         trans.callback.call(trans.scope||window, null, trans.arg, false);
25677     }
25678 });/*
25679  * Based on:
25680  * Ext JS Library 1.1.1
25681  * Copyright(c) 2006-2007, Ext JS, LLC.
25682  *
25683  * Originally Released Under LGPL - original licence link has changed is not relivant.
25684  *
25685  * Fork - LGPL
25686  * <script type="text/javascript">
25687  */
25688
25689 /**
25690  * @class Roo.data.JsonReader
25691  * @extends Roo.data.DataReader
25692  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25693  * based on mappings in a provided Roo.data.Record constructor.
25694  * 
25695  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25696  * in the reply previously. 
25697  * 
25698  * <p>
25699  * Example code:
25700  * <pre><code>
25701 var RecordDef = Roo.data.Record.create([
25702     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25703     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25704 ]);
25705 var myReader = new Roo.data.JsonReader({
25706     totalProperty: "results",    // The property which contains the total dataset size (optional)
25707     root: "rows",                // The property which contains an Array of row objects
25708     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25709 }, RecordDef);
25710 </code></pre>
25711  * <p>
25712  * This would consume a JSON file like this:
25713  * <pre><code>
25714 { 'results': 2, 'rows': [
25715     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25716     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25717 }
25718 </code></pre>
25719  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25720  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25721  * paged from the remote server.
25722  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25723  * @cfg {String} root name of the property which contains the Array of row objects.
25724  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25725  * @cfg {Array} fields Array of field definition objects
25726  * @constructor
25727  * Create a new JsonReader
25728  * @param {Object} meta Metadata configuration options
25729  * @param {Object} recordType Either an Array of field definition objects,
25730  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25731  */
25732 Roo.data.JsonReader = function(meta, recordType){
25733     
25734     meta = meta || {};
25735     // set some defaults:
25736     Roo.applyIf(meta, {
25737         totalProperty: 'total',
25738         successProperty : 'success',
25739         root : 'data',
25740         id : 'id'
25741     });
25742     
25743     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25744 };
25745 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25746     
25747     readerType : 'Json',
25748     
25749     /**
25750      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25751      * Used by Store query builder to append _requestMeta to params.
25752      * 
25753      */
25754     metaFromRemote : false,
25755     /**
25756      * This method is only used by a DataProxy which has retrieved data from a remote server.
25757      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25758      * @return {Object} data A data block which is used by an Roo.data.Store object as
25759      * a cache of Roo.data.Records.
25760      */
25761     read : function(response){
25762         var json = response.responseText;
25763        
25764         var o = /* eval:var:o */ eval("("+json+")");
25765         if(!o) {
25766             throw {message: "JsonReader.read: Json object not found"};
25767         }
25768         
25769         if(o.metaData){
25770             
25771             delete this.ef;
25772             this.metaFromRemote = true;
25773             this.meta = o.metaData;
25774             this.recordType = Roo.data.Record.create(o.metaData.fields);
25775             this.onMetaChange(this.meta, this.recordType, o);
25776         }
25777         return this.readRecords(o);
25778     },
25779
25780     // private function a store will implement
25781     onMetaChange : function(meta, recordType, o){
25782
25783     },
25784
25785     /**
25786          * @ignore
25787          */
25788     simpleAccess: function(obj, subsc) {
25789         return obj[subsc];
25790     },
25791
25792         /**
25793          * @ignore
25794          */
25795     getJsonAccessor: function(){
25796         var re = /[\[\.]/;
25797         return function(expr) {
25798             try {
25799                 return(re.test(expr))
25800                     ? new Function("obj", "return obj." + expr)
25801                     : function(obj){
25802                         return obj[expr];
25803                     };
25804             } catch(e){}
25805             return Roo.emptyFn;
25806         };
25807     }(),
25808
25809     /**
25810      * Create a data block containing Roo.data.Records from an XML document.
25811      * @param {Object} o An object which contains an Array of row objects in the property specified
25812      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25813      * which contains the total size of the dataset.
25814      * @return {Object} data A data block which is used by an Roo.data.Store object as
25815      * a cache of Roo.data.Records.
25816      */
25817     readRecords : function(o){
25818         /**
25819          * After any data loads, the raw JSON data is available for further custom processing.
25820          * @type Object
25821          */
25822         this.o = o;
25823         var s = this.meta, Record = this.recordType,
25824             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25825
25826 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25827         if (!this.ef) {
25828             if(s.totalProperty) {
25829                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25830                 }
25831                 if(s.successProperty) {
25832                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25833                 }
25834                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25835                 if (s.id) {
25836                         var g = this.getJsonAccessor(s.id);
25837                         this.getId = function(rec) {
25838                                 var r = g(rec);  
25839                                 return (r === undefined || r === "") ? null : r;
25840                         };
25841                 } else {
25842                         this.getId = function(){return null;};
25843                 }
25844             this.ef = [];
25845             for(var jj = 0; jj < fl; jj++){
25846                 f = fi[jj];
25847                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25848                 this.ef[jj] = this.getJsonAccessor(map);
25849             }
25850         }
25851
25852         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25853         if(s.totalProperty){
25854             var vt = parseInt(this.getTotal(o), 10);
25855             if(!isNaN(vt)){
25856                 totalRecords = vt;
25857             }
25858         }
25859         if(s.successProperty){
25860             var vs = this.getSuccess(o);
25861             if(vs === false || vs === 'false'){
25862                 success = false;
25863             }
25864         }
25865         var records = [];
25866         for(var i = 0; i < c; i++){
25867             var n = root[i];
25868             var values = {};
25869             var id = this.getId(n);
25870             for(var j = 0; j < fl; j++){
25871                 f = fi[j];
25872                                 var v = this.ef[j](n);
25873                                 if (!f.convert) {
25874                                         Roo.log('missing convert for ' + f.name);
25875                                         Roo.log(f);
25876                                         continue;
25877                                 }
25878                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25879             }
25880                         if (!Record) {
25881                                 return {
25882                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25883                                         success : false,
25884                                         records : [],
25885                                         totalRecords : 0
25886                                 };
25887                         }
25888             var record = new Record(values, id);
25889             record.json = n;
25890             records[i] = record;
25891         }
25892         return {
25893             raw : o,
25894             success : success,
25895             records : records,
25896             totalRecords : totalRecords
25897         };
25898     },
25899     // used when loading children.. @see loadDataFromChildren
25900     toLoadData: function(rec)
25901     {
25902         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25903         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25904         return { data : data, total : data.length };
25905         
25906     }
25907 });/*
25908  * Based on:
25909  * Ext JS Library 1.1.1
25910  * Copyright(c) 2006-2007, Ext JS, LLC.
25911  *
25912  * Originally Released Under LGPL - original licence link has changed is not relivant.
25913  *
25914  * Fork - LGPL
25915  * <script type="text/javascript">
25916  */
25917
25918 /**
25919  * @class Roo.data.XmlReader
25920  * @extends Roo.data.DataReader
25921  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25922  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25923  * <p>
25924  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25925  * header in the HTTP response must be set to "text/xml".</em>
25926  * <p>
25927  * Example code:
25928  * <pre><code>
25929 var RecordDef = Roo.data.Record.create([
25930    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25931    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25932 ]);
25933 var myReader = new Roo.data.XmlReader({
25934    totalRecords: "results", // The element which contains the total dataset size (optional)
25935    record: "row",           // The repeated element which contains row information
25936    id: "id"                 // The element within the row that provides an ID for the record (optional)
25937 }, RecordDef);
25938 </code></pre>
25939  * <p>
25940  * This would consume an XML file like this:
25941  * <pre><code>
25942 &lt;?xml?>
25943 &lt;dataset>
25944  &lt;results>2&lt;/results>
25945  &lt;row>
25946    &lt;id>1&lt;/id>
25947    &lt;name>Bill&lt;/name>
25948    &lt;occupation>Gardener&lt;/occupation>
25949  &lt;/row>
25950  &lt;row>
25951    &lt;id>2&lt;/id>
25952    &lt;name>Ben&lt;/name>
25953    &lt;occupation>Horticulturalist&lt;/occupation>
25954  &lt;/row>
25955 &lt;/dataset>
25956 </code></pre>
25957  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25958  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25959  * paged from the remote server.
25960  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25961  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25962  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25963  * a record identifier value.
25964  * @constructor
25965  * Create a new XmlReader
25966  * @param {Object} meta Metadata configuration options
25967  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25968  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25969  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25970  */
25971 Roo.data.XmlReader = function(meta, recordType){
25972     meta = meta || {};
25973     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25974 };
25975 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25976     
25977     readerType : 'Xml',
25978     
25979     /**
25980      * This method is only used by a DataProxy which has retrieved data from a remote server.
25981          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25982          * to contain a method called 'responseXML' that returns an XML document object.
25983      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25984      * a cache of Roo.data.Records.
25985      */
25986     read : function(response){
25987         var doc = response.responseXML;
25988         if(!doc) {
25989             throw {message: "XmlReader.read: XML Document not available"};
25990         }
25991         return this.readRecords(doc);
25992     },
25993
25994     /**
25995      * Create a data block containing Roo.data.Records from an XML document.
25996          * @param {Object} doc A parsed XML document.
25997      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25998      * a cache of Roo.data.Records.
25999      */
26000     readRecords : function(doc){
26001         /**
26002          * After any data loads/reads, the raw XML Document is available for further custom processing.
26003          * @type XMLDocument
26004          */
26005         this.xmlData = doc;
26006         var root = doc.documentElement || doc;
26007         var q = Roo.DomQuery;
26008         var recordType = this.recordType, fields = recordType.prototype.fields;
26009         var sid = this.meta.id;
26010         var totalRecords = 0, success = true;
26011         if(this.meta.totalRecords){
26012             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26013         }
26014         
26015         if(this.meta.success){
26016             var sv = q.selectValue(this.meta.success, root, true);
26017             success = sv !== false && sv !== 'false';
26018         }
26019         var records = [];
26020         var ns = q.select(this.meta.record, root);
26021         for(var i = 0, len = ns.length; i < len; i++) {
26022                 var n = ns[i];
26023                 var values = {};
26024                 var id = sid ? q.selectValue(sid, n) : undefined;
26025                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26026                     var f = fields.items[j];
26027                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26028                     v = f.convert(v);
26029                     values[f.name] = v;
26030                 }
26031                 var record = new recordType(values, id);
26032                 record.node = n;
26033                 records[records.length] = record;
26034             }
26035
26036             return {
26037                 success : success,
26038                 records : records,
26039                 totalRecords : totalRecords || records.length
26040             };
26041     }
26042 });/*
26043  * Based on:
26044  * Ext JS Library 1.1.1
26045  * Copyright(c) 2006-2007, Ext JS, LLC.
26046  *
26047  * Originally Released Under LGPL - original licence link has changed is not relivant.
26048  *
26049  * Fork - LGPL
26050  * <script type="text/javascript">
26051  */
26052
26053 /**
26054  * @class Roo.data.ArrayReader
26055  * @extends Roo.data.DataReader
26056  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26057  * Each element of that Array represents a row of data fields. The
26058  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26059  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26060  * <p>
26061  * Example code:.
26062  * <pre><code>
26063 var RecordDef = Roo.data.Record.create([
26064     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26065     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26066 ]);
26067 var myReader = new Roo.data.ArrayReader({
26068     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26069 }, RecordDef);
26070 </code></pre>
26071  * <p>
26072  * This would consume an Array like this:
26073  * <pre><code>
26074 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26075   </code></pre>
26076  
26077  * @constructor
26078  * Create a new JsonReader
26079  * @param {Object} meta Metadata configuration options.
26080  * @param {Object|Array} recordType Either an Array of field definition objects
26081  * 
26082  * @cfg {Array} fields Array of field definition objects
26083  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26084  * as specified to {@link Roo.data.Record#create},
26085  * or an {@link Roo.data.Record} object
26086  *
26087  * 
26088  * created using {@link Roo.data.Record#create}.
26089  */
26090 Roo.data.ArrayReader = function(meta, recordType)
26091 {    
26092     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26093 };
26094
26095 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26096     
26097       /**
26098      * Create a data block containing Roo.data.Records from an XML document.
26099      * @param {Object} o An Array of row objects which represents the dataset.
26100      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26101      * a cache of Roo.data.Records.
26102      */
26103     readRecords : function(o)
26104     {
26105         var sid = this.meta ? this.meta.id : null;
26106         var recordType = this.recordType, fields = recordType.prototype.fields;
26107         var records = [];
26108         var root = o;
26109         for(var i = 0; i < root.length; i++){
26110             var n = root[i];
26111             var values = {};
26112             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26113             for(var j = 0, jlen = fields.length; j < jlen; j++){
26114                 var f = fields.items[j];
26115                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26116                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26117                 v = f.convert(v);
26118                 values[f.name] = v;
26119             }
26120             var record = new recordType(values, id);
26121             record.json = n;
26122             records[records.length] = record;
26123         }
26124         return {
26125             records : records,
26126             totalRecords : records.length
26127         };
26128     },
26129     // used when loading children.. @see loadDataFromChildren
26130     toLoadData: function(rec)
26131     {
26132         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26133         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26134         
26135     }
26136     
26137     
26138 });/*
26139  * Based on:
26140  * Ext JS Library 1.1.1
26141  * Copyright(c) 2006-2007, Ext JS, LLC.
26142  *
26143  * Originally Released Under LGPL - original licence link has changed is not relivant.
26144  *
26145  * Fork - LGPL
26146  * <script type="text/javascript">
26147  */
26148
26149
26150 /**
26151  * @class Roo.data.Tree
26152  * @extends Roo.util.Observable
26153  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26154  * in the tree have most standard DOM functionality.
26155  * @constructor
26156  * @param {Node} root (optional) The root node
26157  */
26158 Roo.data.Tree = function(root){
26159    this.nodeHash = {};
26160    /**
26161     * The root node for this tree
26162     * @type Node
26163     */
26164    this.root = null;
26165    if(root){
26166        this.setRootNode(root);
26167    }
26168    this.addEvents({
26169        /**
26170         * @event append
26171         * Fires when a new child node is appended to a node in this tree.
26172         * @param {Tree} tree The owner tree
26173         * @param {Node} parent The parent node
26174         * @param {Node} node The newly appended node
26175         * @param {Number} index The index of the newly appended node
26176         */
26177        "append" : true,
26178        /**
26179         * @event remove
26180         * Fires when a child node is removed from a node in this tree.
26181         * @param {Tree} tree The owner tree
26182         * @param {Node} parent The parent node
26183         * @param {Node} node The child node removed
26184         */
26185        "remove" : true,
26186        /**
26187         * @event move
26188         * Fires when a node is moved to a new location in the tree
26189         * @param {Tree} tree The owner tree
26190         * @param {Node} node The node moved
26191         * @param {Node} oldParent The old parent of this node
26192         * @param {Node} newParent The new parent of this node
26193         * @param {Number} index The index it was moved to
26194         */
26195        "move" : true,
26196        /**
26197         * @event insert
26198         * Fires when a new child node is inserted in a node in this tree.
26199         * @param {Tree} tree The owner tree
26200         * @param {Node} parent The parent node
26201         * @param {Node} node The child node inserted
26202         * @param {Node} refNode The child node the node was inserted before
26203         */
26204        "insert" : true,
26205        /**
26206         * @event beforeappend
26207         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26208         * @param {Tree} tree The owner tree
26209         * @param {Node} parent The parent node
26210         * @param {Node} node The child node to be appended
26211         */
26212        "beforeappend" : true,
26213        /**
26214         * @event beforeremove
26215         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26216         * @param {Tree} tree The owner tree
26217         * @param {Node} parent The parent node
26218         * @param {Node} node The child node to be removed
26219         */
26220        "beforeremove" : true,
26221        /**
26222         * @event beforemove
26223         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26224         * @param {Tree} tree The owner tree
26225         * @param {Node} node The node being moved
26226         * @param {Node} oldParent The parent of the node
26227         * @param {Node} newParent The new parent the node is moving to
26228         * @param {Number} index The index it is being moved to
26229         */
26230        "beforemove" : true,
26231        /**
26232         * @event beforeinsert
26233         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26234         * @param {Tree} tree The owner tree
26235         * @param {Node} parent The parent node
26236         * @param {Node} node The child node to be inserted
26237         * @param {Node} refNode The child node the node is being inserted before
26238         */
26239        "beforeinsert" : true
26240    });
26241
26242     Roo.data.Tree.superclass.constructor.call(this);
26243 };
26244
26245 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26246     pathSeparator: "/",
26247
26248     proxyNodeEvent : function(){
26249         return this.fireEvent.apply(this, arguments);
26250     },
26251
26252     /**
26253      * Returns the root node for this tree.
26254      * @return {Node}
26255      */
26256     getRootNode : function(){
26257         return this.root;
26258     },
26259
26260     /**
26261      * Sets the root node for this tree.
26262      * @param {Node} node
26263      * @return {Node}
26264      */
26265     setRootNode : function(node){
26266         this.root = node;
26267         node.ownerTree = this;
26268         node.isRoot = true;
26269         this.registerNode(node);
26270         return node;
26271     },
26272
26273     /**
26274      * Gets a node in this tree by its id.
26275      * @param {String} id
26276      * @return {Node}
26277      */
26278     getNodeById : function(id){
26279         return this.nodeHash[id];
26280     },
26281
26282     registerNode : function(node){
26283         this.nodeHash[node.id] = node;
26284     },
26285
26286     unregisterNode : function(node){
26287         delete this.nodeHash[node.id];
26288     },
26289
26290     toString : function(){
26291         return "[Tree"+(this.id?" "+this.id:"")+"]";
26292     }
26293 });
26294
26295 /**
26296  * @class Roo.data.Node
26297  * @extends Roo.util.Observable
26298  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26299  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26300  * @constructor
26301  * @param {Object} attributes The attributes/config for the node
26302  */
26303 Roo.data.Node = function(attributes){
26304     /**
26305      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26306      * @type {Object}
26307      */
26308     this.attributes = attributes || {};
26309     this.leaf = this.attributes.leaf;
26310     /**
26311      * The node id. @type String
26312      */
26313     this.id = this.attributes.id;
26314     if(!this.id){
26315         this.id = Roo.id(null, "ynode-");
26316         this.attributes.id = this.id;
26317     }
26318      
26319     
26320     /**
26321      * All child nodes of this node. @type Array
26322      */
26323     this.childNodes = [];
26324     if(!this.childNodes.indexOf){ // indexOf is a must
26325         this.childNodes.indexOf = function(o){
26326             for(var i = 0, len = this.length; i < len; i++){
26327                 if(this[i] == o) {
26328                     return i;
26329                 }
26330             }
26331             return -1;
26332         };
26333     }
26334     /**
26335      * The parent node for this node. @type Node
26336      */
26337     this.parentNode = null;
26338     /**
26339      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26340      */
26341     this.firstChild = null;
26342     /**
26343      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26344      */
26345     this.lastChild = null;
26346     /**
26347      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26348      */
26349     this.previousSibling = null;
26350     /**
26351      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26352      */
26353     this.nextSibling = null;
26354
26355     this.addEvents({
26356        /**
26357         * @event append
26358         * Fires when a new child node is appended
26359         * @param {Tree} tree The owner tree
26360         * @param {Node} this This node
26361         * @param {Node} node The newly appended node
26362         * @param {Number} index The index of the newly appended node
26363         */
26364        "append" : true,
26365        /**
26366         * @event remove
26367         * Fires when a child node is removed
26368         * @param {Tree} tree The owner tree
26369         * @param {Node} this This node
26370         * @param {Node} node The removed node
26371         */
26372        "remove" : true,
26373        /**
26374         * @event move
26375         * Fires when this node is moved to a new location in the tree
26376         * @param {Tree} tree The owner tree
26377         * @param {Node} this This node
26378         * @param {Node} oldParent The old parent of this node
26379         * @param {Node} newParent The new parent of this node
26380         * @param {Number} index The index it was moved to
26381         */
26382        "move" : true,
26383        /**
26384         * @event insert
26385         * Fires when a new child node is inserted.
26386         * @param {Tree} tree The owner tree
26387         * @param {Node} this This node
26388         * @param {Node} node The child node inserted
26389         * @param {Node} refNode The child node the node was inserted before
26390         */
26391        "insert" : true,
26392        /**
26393         * @event beforeappend
26394         * Fires before a new child is appended, return false to cancel the append.
26395         * @param {Tree} tree The owner tree
26396         * @param {Node} this This node
26397         * @param {Node} node The child node to be appended
26398         */
26399        "beforeappend" : true,
26400        /**
26401         * @event beforeremove
26402         * Fires before a child is removed, return false to cancel the remove.
26403         * @param {Tree} tree The owner tree
26404         * @param {Node} this This node
26405         * @param {Node} node The child node to be removed
26406         */
26407        "beforeremove" : true,
26408        /**
26409         * @event beforemove
26410         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26411         * @param {Tree} tree The owner tree
26412         * @param {Node} this This node
26413         * @param {Node} oldParent The parent of this node
26414         * @param {Node} newParent The new parent this node is moving to
26415         * @param {Number} index The index it is being moved to
26416         */
26417        "beforemove" : true,
26418        /**
26419         * @event beforeinsert
26420         * Fires before a new child is inserted, return false to cancel the insert.
26421         * @param {Tree} tree The owner tree
26422         * @param {Node} this This node
26423         * @param {Node} node The child node to be inserted
26424         * @param {Node} refNode The child node the node is being inserted before
26425         */
26426        "beforeinsert" : true
26427    });
26428     this.listeners = this.attributes.listeners;
26429     Roo.data.Node.superclass.constructor.call(this);
26430 };
26431
26432 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26433     fireEvent : function(evtName){
26434         // first do standard event for this node
26435         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26436             return false;
26437         }
26438         // then bubble it up to the tree if the event wasn't cancelled
26439         var ot = this.getOwnerTree();
26440         if(ot){
26441             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26442                 return false;
26443             }
26444         }
26445         return true;
26446     },
26447
26448     /**
26449      * Returns true if this node is a leaf
26450      * @return {Boolean}
26451      */
26452     isLeaf : function(){
26453         return this.leaf === true;
26454     },
26455
26456     // private
26457     setFirstChild : function(node){
26458         this.firstChild = node;
26459     },
26460
26461     //private
26462     setLastChild : function(node){
26463         this.lastChild = node;
26464     },
26465
26466
26467     /**
26468      * Returns true if this node is the last child of its parent
26469      * @return {Boolean}
26470      */
26471     isLast : function(){
26472        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26473     },
26474
26475     /**
26476      * Returns true if this node is the first child of its parent
26477      * @return {Boolean}
26478      */
26479     isFirst : function(){
26480        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26481     },
26482
26483     hasChildNodes : function(){
26484         return !this.isLeaf() && this.childNodes.length > 0;
26485     },
26486
26487     /**
26488      * Insert node(s) as the last child node of this node.
26489      * @param {Node/Array} node The node or Array of nodes to append
26490      * @return {Node} The appended node if single append, or null if an array was passed
26491      */
26492     appendChild : function(node){
26493         var multi = false;
26494         if(node instanceof Array){
26495             multi = node;
26496         }else if(arguments.length > 1){
26497             multi = arguments;
26498         }
26499         
26500         // if passed an array or multiple args do them one by one
26501         if(multi){
26502             for(var i = 0, len = multi.length; i < len; i++) {
26503                 this.appendChild(multi[i]);
26504             }
26505         }else{
26506             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26507                 return false;
26508             }
26509             var index = this.childNodes.length;
26510             var oldParent = node.parentNode;
26511             // it's a move, make sure we move it cleanly
26512             if(oldParent){
26513                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26514                     return false;
26515                 }
26516                 oldParent.removeChild(node);
26517             }
26518             
26519             index = this.childNodes.length;
26520             if(index == 0){
26521                 this.setFirstChild(node);
26522             }
26523             this.childNodes.push(node);
26524             node.parentNode = this;
26525             var ps = this.childNodes[index-1];
26526             if(ps){
26527                 node.previousSibling = ps;
26528                 ps.nextSibling = node;
26529             }else{
26530                 node.previousSibling = null;
26531             }
26532             node.nextSibling = null;
26533             this.setLastChild(node);
26534             node.setOwnerTree(this.getOwnerTree());
26535             this.fireEvent("append", this.ownerTree, this, node, index);
26536             if(this.ownerTree) {
26537                 this.ownerTree.fireEvent("appendnode", this, node, index);
26538             }
26539             if(oldParent){
26540                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26541             }
26542             return node;
26543         }
26544     },
26545
26546     /**
26547      * Removes a child node from this node.
26548      * @param {Node} node The node to remove
26549      * @return {Node} The removed node
26550      */
26551     removeChild : function(node){
26552         var index = this.childNodes.indexOf(node);
26553         if(index == -1){
26554             return false;
26555         }
26556         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26557             return false;
26558         }
26559
26560         // remove it from childNodes collection
26561         this.childNodes.splice(index, 1);
26562
26563         // update siblings
26564         if(node.previousSibling){
26565             node.previousSibling.nextSibling = node.nextSibling;
26566         }
26567         if(node.nextSibling){
26568             node.nextSibling.previousSibling = node.previousSibling;
26569         }
26570
26571         // update child refs
26572         if(this.firstChild == node){
26573             this.setFirstChild(node.nextSibling);
26574         }
26575         if(this.lastChild == node){
26576             this.setLastChild(node.previousSibling);
26577         }
26578
26579         node.setOwnerTree(null);
26580         // clear any references from the node
26581         node.parentNode = null;
26582         node.previousSibling = null;
26583         node.nextSibling = null;
26584         this.fireEvent("remove", this.ownerTree, this, node);
26585         return node;
26586     },
26587
26588     /**
26589      * Inserts the first node before the second node in this nodes childNodes collection.
26590      * @param {Node} node The node to insert
26591      * @param {Node} refNode The node to insert before (if null the node is appended)
26592      * @return {Node} The inserted node
26593      */
26594     insertBefore : function(node, refNode){
26595         if(!refNode){ // like standard Dom, refNode can be null for append
26596             return this.appendChild(node);
26597         }
26598         // nothing to do
26599         if(node == refNode){
26600             return false;
26601         }
26602
26603         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26604             return false;
26605         }
26606         var index = this.childNodes.indexOf(refNode);
26607         var oldParent = node.parentNode;
26608         var refIndex = index;
26609
26610         // when moving internally, indexes will change after remove
26611         if(oldParent == this && this.childNodes.indexOf(node) < index){
26612             refIndex--;
26613         }
26614
26615         // it's a move, make sure we move it cleanly
26616         if(oldParent){
26617             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26618                 return false;
26619             }
26620             oldParent.removeChild(node);
26621         }
26622         if(refIndex == 0){
26623             this.setFirstChild(node);
26624         }
26625         this.childNodes.splice(refIndex, 0, node);
26626         node.parentNode = this;
26627         var ps = this.childNodes[refIndex-1];
26628         if(ps){
26629             node.previousSibling = ps;
26630             ps.nextSibling = node;
26631         }else{
26632             node.previousSibling = null;
26633         }
26634         node.nextSibling = refNode;
26635         refNode.previousSibling = node;
26636         node.setOwnerTree(this.getOwnerTree());
26637         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26638         if(oldParent){
26639             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26640         }
26641         return node;
26642     },
26643
26644     /**
26645      * Returns the child node at the specified index.
26646      * @param {Number} index
26647      * @return {Node}
26648      */
26649     item : function(index){
26650         return this.childNodes[index];
26651     },
26652
26653     /**
26654      * Replaces one child node in this node with another.
26655      * @param {Node} newChild The replacement node
26656      * @param {Node} oldChild The node to replace
26657      * @return {Node} The replaced node
26658      */
26659     replaceChild : function(newChild, oldChild){
26660         this.insertBefore(newChild, oldChild);
26661         this.removeChild(oldChild);
26662         return oldChild;
26663     },
26664
26665     /**
26666      * Returns the index of a child node
26667      * @param {Node} node
26668      * @return {Number} The index of the node or -1 if it was not found
26669      */
26670     indexOf : function(child){
26671         return this.childNodes.indexOf(child);
26672     },
26673
26674     /**
26675      * Returns the tree this node is in.
26676      * @return {Tree}
26677      */
26678     getOwnerTree : function(){
26679         // if it doesn't have one, look for one
26680         if(!this.ownerTree){
26681             var p = this;
26682             while(p){
26683                 if(p.ownerTree){
26684                     this.ownerTree = p.ownerTree;
26685                     break;
26686                 }
26687                 p = p.parentNode;
26688             }
26689         }
26690         return this.ownerTree;
26691     },
26692
26693     /**
26694      * Returns depth of this node (the root node has a depth of 0)
26695      * @return {Number}
26696      */
26697     getDepth : function(){
26698         var depth = 0;
26699         var p = this;
26700         while(p.parentNode){
26701             ++depth;
26702             p = p.parentNode;
26703         }
26704         return depth;
26705     },
26706
26707     // private
26708     setOwnerTree : function(tree){
26709         // if it's move, we need to update everyone
26710         if(tree != this.ownerTree){
26711             if(this.ownerTree){
26712                 this.ownerTree.unregisterNode(this);
26713             }
26714             this.ownerTree = tree;
26715             var cs = this.childNodes;
26716             for(var i = 0, len = cs.length; i < len; i++) {
26717                 cs[i].setOwnerTree(tree);
26718             }
26719             if(tree){
26720                 tree.registerNode(this);
26721             }
26722         }
26723     },
26724
26725     /**
26726      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26727      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26728      * @return {String} The path
26729      */
26730     getPath : function(attr){
26731         attr = attr || "id";
26732         var p = this.parentNode;
26733         var b = [this.attributes[attr]];
26734         while(p){
26735             b.unshift(p.attributes[attr]);
26736             p = p.parentNode;
26737         }
26738         var sep = this.getOwnerTree().pathSeparator;
26739         return sep + b.join(sep);
26740     },
26741
26742     /**
26743      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26744      * function call will be the scope provided or the current node. The arguments to the function
26745      * will be the args provided or the current node. If the function returns false at any point,
26746      * the bubble is stopped.
26747      * @param {Function} fn The function to call
26748      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26749      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26750      */
26751     bubble : function(fn, scope, args){
26752         var p = this;
26753         while(p){
26754             if(fn.call(scope || p, args || p) === false){
26755                 break;
26756             }
26757             p = p.parentNode;
26758         }
26759     },
26760
26761     /**
26762      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26763      * function call will be the scope provided or the current node. The arguments to the function
26764      * will be the args provided or the current node. If the function returns false at any point,
26765      * the cascade is stopped on that branch.
26766      * @param {Function} fn The function to call
26767      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26768      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26769      */
26770     cascade : function(fn, scope, args){
26771         if(fn.call(scope || this, args || this) !== false){
26772             var cs = this.childNodes;
26773             for(var i = 0, len = cs.length; i < len; i++) {
26774                 cs[i].cascade(fn, scope, args);
26775             }
26776         }
26777     },
26778
26779     /**
26780      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26781      * function call will be the scope provided or the current node. The arguments to the function
26782      * will be the args provided or the current node. If the function returns false at any point,
26783      * the iteration stops.
26784      * @param {Function} fn The function to call
26785      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26786      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26787      */
26788     eachChild : function(fn, scope, args){
26789         var cs = this.childNodes;
26790         for(var i = 0, len = cs.length; i < len; i++) {
26791                 if(fn.call(scope || this, args || cs[i]) === false){
26792                     break;
26793                 }
26794         }
26795     },
26796
26797     /**
26798      * Finds the first child that has the attribute with the specified value.
26799      * @param {String} attribute The attribute name
26800      * @param {Mixed} value The value to search for
26801      * @return {Node} The found child or null if none was found
26802      */
26803     findChild : function(attribute, value){
26804         var cs = this.childNodes;
26805         for(var i = 0, len = cs.length; i < len; i++) {
26806                 if(cs[i].attributes[attribute] == value){
26807                     return cs[i];
26808                 }
26809         }
26810         return null;
26811     },
26812
26813     /**
26814      * Finds the first child by a custom function. The child matches if the function passed
26815      * returns true.
26816      * @param {Function} fn
26817      * @param {Object} scope (optional)
26818      * @return {Node} The found child or null if none was found
26819      */
26820     findChildBy : function(fn, scope){
26821         var cs = this.childNodes;
26822         for(var i = 0, len = cs.length; i < len; i++) {
26823                 if(fn.call(scope||cs[i], cs[i]) === true){
26824                     return cs[i];
26825                 }
26826         }
26827         return null;
26828     },
26829
26830     /**
26831      * Sorts this nodes children using the supplied sort function
26832      * @param {Function} fn
26833      * @param {Object} scope (optional)
26834      */
26835     sort : function(fn, scope){
26836         var cs = this.childNodes;
26837         var len = cs.length;
26838         if(len > 0){
26839             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26840             cs.sort(sortFn);
26841             for(var i = 0; i < len; i++){
26842                 var n = cs[i];
26843                 n.previousSibling = cs[i-1];
26844                 n.nextSibling = cs[i+1];
26845                 if(i == 0){
26846                     this.setFirstChild(n);
26847                 }
26848                 if(i == len-1){
26849                     this.setLastChild(n);
26850                 }
26851             }
26852         }
26853     },
26854
26855     /**
26856      * Returns true if this node is an ancestor (at any point) of the passed node.
26857      * @param {Node} node
26858      * @return {Boolean}
26859      */
26860     contains : function(node){
26861         return node.isAncestor(this);
26862     },
26863
26864     /**
26865      * Returns true if the passed node is an ancestor (at any point) of this node.
26866      * @param {Node} node
26867      * @return {Boolean}
26868      */
26869     isAncestor : function(node){
26870         var p = this.parentNode;
26871         while(p){
26872             if(p == node){
26873                 return true;
26874             }
26875             p = p.parentNode;
26876         }
26877         return false;
26878     },
26879
26880     toString : function(){
26881         return "[Node"+(this.id?" "+this.id:"")+"]";
26882     }
26883 });/*
26884  * Based on:
26885  * Ext JS Library 1.1.1
26886  * Copyright(c) 2006-2007, Ext JS, LLC.
26887  *
26888  * Originally Released Under LGPL - original licence link has changed is not relivant.
26889  *
26890  * Fork - LGPL
26891  * <script type="text/javascript">
26892  */
26893
26894
26895 /**
26896  * @class Roo.Shadow
26897  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26898  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26899  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26900  * @constructor
26901  * Create a new Shadow
26902  * @param {Object} config The config object
26903  */
26904 Roo.Shadow = function(config){
26905     Roo.apply(this, config);
26906     if(typeof this.mode != "string"){
26907         this.mode = this.defaultMode;
26908     }
26909     var o = this.offset, a = {h: 0};
26910     var rad = Math.floor(this.offset/2);
26911     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26912         case "drop":
26913             a.w = 0;
26914             a.l = a.t = o;
26915             a.t -= 1;
26916             if(Roo.isIE){
26917                 a.l -= this.offset + rad;
26918                 a.t -= this.offset + rad;
26919                 a.w -= rad;
26920                 a.h -= rad;
26921                 a.t += 1;
26922             }
26923         break;
26924         case "sides":
26925             a.w = (o*2);
26926             a.l = -o;
26927             a.t = o-1;
26928             if(Roo.isIE){
26929                 a.l -= (this.offset - rad);
26930                 a.t -= this.offset + rad;
26931                 a.l += 1;
26932                 a.w -= (this.offset - rad)*2;
26933                 a.w -= rad + 1;
26934                 a.h -= 1;
26935             }
26936         break;
26937         case "frame":
26938             a.w = a.h = (o*2);
26939             a.l = a.t = -o;
26940             a.t += 1;
26941             a.h -= 2;
26942             if(Roo.isIE){
26943                 a.l -= (this.offset - rad);
26944                 a.t -= (this.offset - rad);
26945                 a.l += 1;
26946                 a.w -= (this.offset + rad + 1);
26947                 a.h -= (this.offset + rad);
26948                 a.h += 1;
26949             }
26950         break;
26951     };
26952
26953     this.adjusts = a;
26954 };
26955
26956 Roo.Shadow.prototype = {
26957     /**
26958      * @cfg {String} mode
26959      * The shadow display mode.  Supports the following options:<br />
26960      * sides: Shadow displays on both sides and bottom only<br />
26961      * frame: Shadow displays equally on all four sides<br />
26962      * drop: Traditional bottom-right drop shadow (default)
26963      */
26964     mode: false,
26965     /**
26966      * @cfg {String} offset
26967      * The number of pixels to offset the shadow from the element (defaults to 4)
26968      */
26969     offset: 4,
26970
26971     // private
26972     defaultMode: "drop",
26973
26974     /**
26975      * Displays the shadow under the target element
26976      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26977      */
26978     show : function(target){
26979         target = Roo.get(target);
26980         if(!this.el){
26981             this.el = Roo.Shadow.Pool.pull();
26982             if(this.el.dom.nextSibling != target.dom){
26983                 this.el.insertBefore(target);
26984             }
26985         }
26986         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26987         if(Roo.isIE){
26988             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26989         }
26990         this.realign(
26991             target.getLeft(true),
26992             target.getTop(true),
26993             target.getWidth(),
26994             target.getHeight()
26995         );
26996         this.el.dom.style.display = "block";
26997     },
26998
26999     /**
27000      * Returns true if the shadow is visible, else false
27001      */
27002     isVisible : function(){
27003         return this.el ? true : false;  
27004     },
27005
27006     /**
27007      * Direct alignment when values are already available. Show must be called at least once before
27008      * calling this method to ensure it is initialized.
27009      * @param {Number} left The target element left position
27010      * @param {Number} top The target element top position
27011      * @param {Number} width The target element width
27012      * @param {Number} height The target element height
27013      */
27014     realign : function(l, t, w, h){
27015         if(!this.el){
27016             return;
27017         }
27018         var a = this.adjusts, d = this.el.dom, s = d.style;
27019         var iea = 0;
27020         s.left = (l+a.l)+"px";
27021         s.top = (t+a.t)+"px";
27022         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27023  
27024         if(s.width != sws || s.height != shs){
27025             s.width = sws;
27026             s.height = shs;
27027             if(!Roo.isIE){
27028                 var cn = d.childNodes;
27029                 var sww = Math.max(0, (sw-12))+"px";
27030                 cn[0].childNodes[1].style.width = sww;
27031                 cn[1].childNodes[1].style.width = sww;
27032                 cn[2].childNodes[1].style.width = sww;
27033                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27034             }
27035         }
27036     },
27037
27038     /**
27039      * Hides this shadow
27040      */
27041     hide : function(){
27042         if(this.el){
27043             this.el.dom.style.display = "none";
27044             Roo.Shadow.Pool.push(this.el);
27045             delete this.el;
27046         }
27047     },
27048
27049     /**
27050      * Adjust the z-index of this shadow
27051      * @param {Number} zindex The new z-index
27052      */
27053     setZIndex : function(z){
27054         this.zIndex = z;
27055         if(this.el){
27056             this.el.setStyle("z-index", z);
27057         }
27058     }
27059 };
27060
27061 // Private utility class that manages the internal Shadow cache
27062 Roo.Shadow.Pool = function(){
27063     var p = [];
27064     var markup = Roo.isIE ?
27065                  '<div class="x-ie-shadow"></div>' :
27066                  '<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>';
27067     return {
27068         pull : function(){
27069             var sh = p.shift();
27070             if(!sh){
27071                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27072                 sh.autoBoxAdjust = false;
27073             }
27074             return sh;
27075         },
27076
27077         push : function(sh){
27078             p.push(sh);
27079         }
27080     };
27081 }();/*
27082  * Based on:
27083  * Ext JS Library 1.1.1
27084  * Copyright(c) 2006-2007, Ext JS, LLC.
27085  *
27086  * Originally Released Under LGPL - original licence link has changed is not relivant.
27087  *
27088  * Fork - LGPL
27089  * <script type="text/javascript">
27090  */
27091
27092
27093 /**
27094  * @class Roo.SplitBar
27095  * @extends Roo.util.Observable
27096  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27097  * <br><br>
27098  * Usage:
27099  * <pre><code>
27100 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27101                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27102 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27103 split.minSize = 100;
27104 split.maxSize = 600;
27105 split.animate = true;
27106 split.on('moved', splitterMoved);
27107 </code></pre>
27108  * @constructor
27109  * Create a new SplitBar
27110  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27111  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27112  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27113  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27114                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27115                         position of the SplitBar).
27116  */
27117 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27118     
27119     /** @private */
27120     this.el = Roo.get(dragElement, true);
27121     this.el.dom.unselectable = "on";
27122     /** @private */
27123     this.resizingEl = Roo.get(resizingElement, true);
27124
27125     /**
27126      * @private
27127      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27128      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27129      * @type Number
27130      */
27131     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27132     
27133     /**
27134      * The minimum size of the resizing element. (Defaults to 0)
27135      * @type Number
27136      */
27137     this.minSize = 0;
27138     
27139     /**
27140      * The maximum size of the resizing element. (Defaults to 2000)
27141      * @type Number
27142      */
27143     this.maxSize = 2000;
27144     
27145     /**
27146      * Whether to animate the transition to the new size
27147      * @type Boolean
27148      */
27149     this.animate = false;
27150     
27151     /**
27152      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27153      * @type Boolean
27154      */
27155     this.useShim = false;
27156     
27157     /** @private */
27158     this.shim = null;
27159     
27160     if(!existingProxy){
27161         /** @private */
27162         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27163     }else{
27164         this.proxy = Roo.get(existingProxy).dom;
27165     }
27166     /** @private */
27167     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27168     
27169     /** @private */
27170     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27171     
27172     /** @private */
27173     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27174     
27175     /** @private */
27176     this.dragSpecs = {};
27177     
27178     /**
27179      * @private The adapter to use to positon and resize elements
27180      */
27181     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27182     this.adapter.init(this);
27183     
27184     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27185         /** @private */
27186         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27187         this.el.addClass("x-splitbar-h");
27188     }else{
27189         /** @private */
27190         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27191         this.el.addClass("x-splitbar-v");
27192     }
27193     
27194     this.addEvents({
27195         /**
27196          * @event resize
27197          * Fires when the splitter is moved (alias for {@link #event-moved})
27198          * @param {Roo.SplitBar} this
27199          * @param {Number} newSize the new width or height
27200          */
27201         "resize" : true,
27202         /**
27203          * @event moved
27204          * Fires when the splitter is moved
27205          * @param {Roo.SplitBar} this
27206          * @param {Number} newSize the new width or height
27207          */
27208         "moved" : true,
27209         /**
27210          * @event beforeresize
27211          * Fires before the splitter is dragged
27212          * @param {Roo.SplitBar} this
27213          */
27214         "beforeresize" : true,
27215
27216         "beforeapply" : true
27217     });
27218
27219     Roo.util.Observable.call(this);
27220 };
27221
27222 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27223     onStartProxyDrag : function(x, y){
27224         this.fireEvent("beforeresize", this);
27225         if(!this.overlay){
27226             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27227             o.unselectable();
27228             o.enableDisplayMode("block");
27229             // all splitbars share the same overlay
27230             Roo.SplitBar.prototype.overlay = o;
27231         }
27232         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27233         this.overlay.show();
27234         Roo.get(this.proxy).setDisplayed("block");
27235         var size = this.adapter.getElementSize(this);
27236         this.activeMinSize = this.getMinimumSize();;
27237         this.activeMaxSize = this.getMaximumSize();;
27238         var c1 = size - this.activeMinSize;
27239         var c2 = Math.max(this.activeMaxSize - size, 0);
27240         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27241             this.dd.resetConstraints();
27242             this.dd.setXConstraint(
27243                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27244                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27245             );
27246             this.dd.setYConstraint(0, 0);
27247         }else{
27248             this.dd.resetConstraints();
27249             this.dd.setXConstraint(0, 0);
27250             this.dd.setYConstraint(
27251                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27252                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27253             );
27254          }
27255         this.dragSpecs.startSize = size;
27256         this.dragSpecs.startPoint = [x, y];
27257         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27258     },
27259     
27260     /** 
27261      * @private Called after the drag operation by the DDProxy
27262      */
27263     onEndProxyDrag : function(e){
27264         Roo.get(this.proxy).setDisplayed(false);
27265         var endPoint = Roo.lib.Event.getXY(e);
27266         if(this.overlay){
27267             this.overlay.hide();
27268         }
27269         var newSize;
27270         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27271             newSize = this.dragSpecs.startSize + 
27272                 (this.placement == Roo.SplitBar.LEFT ?
27273                     endPoint[0] - this.dragSpecs.startPoint[0] :
27274                     this.dragSpecs.startPoint[0] - endPoint[0]
27275                 );
27276         }else{
27277             newSize = this.dragSpecs.startSize + 
27278                 (this.placement == Roo.SplitBar.TOP ?
27279                     endPoint[1] - this.dragSpecs.startPoint[1] :
27280                     this.dragSpecs.startPoint[1] - endPoint[1]
27281                 );
27282         }
27283         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27284         if(newSize != this.dragSpecs.startSize){
27285             if(this.fireEvent('beforeapply', this, newSize) !== false){
27286                 this.adapter.setElementSize(this, newSize);
27287                 this.fireEvent("moved", this, newSize);
27288                 this.fireEvent("resize", this, newSize);
27289             }
27290         }
27291     },
27292     
27293     /**
27294      * Get the adapter this SplitBar uses
27295      * @return The adapter object
27296      */
27297     getAdapter : function(){
27298         return this.adapter;
27299     },
27300     
27301     /**
27302      * Set the adapter this SplitBar uses
27303      * @param {Object} adapter A SplitBar adapter object
27304      */
27305     setAdapter : function(adapter){
27306         this.adapter = adapter;
27307         this.adapter.init(this);
27308     },
27309     
27310     /**
27311      * Gets the minimum size for the resizing element
27312      * @return {Number} The minimum size
27313      */
27314     getMinimumSize : function(){
27315         return this.minSize;
27316     },
27317     
27318     /**
27319      * Sets the minimum size for the resizing element
27320      * @param {Number} minSize The minimum size
27321      */
27322     setMinimumSize : function(minSize){
27323         this.minSize = minSize;
27324     },
27325     
27326     /**
27327      * Gets the maximum size for the resizing element
27328      * @return {Number} The maximum size
27329      */
27330     getMaximumSize : function(){
27331         return this.maxSize;
27332     },
27333     
27334     /**
27335      * Sets the maximum size for the resizing element
27336      * @param {Number} maxSize The maximum size
27337      */
27338     setMaximumSize : function(maxSize){
27339         this.maxSize = maxSize;
27340     },
27341     
27342     /**
27343      * Sets the initialize size for the resizing element
27344      * @param {Number} size The initial size
27345      */
27346     setCurrentSize : function(size){
27347         var oldAnimate = this.animate;
27348         this.animate = false;
27349         this.adapter.setElementSize(this, size);
27350         this.animate = oldAnimate;
27351     },
27352     
27353     /**
27354      * Destroy this splitbar. 
27355      * @param {Boolean} removeEl True to remove the element
27356      */
27357     destroy : function(removeEl){
27358         if(this.shim){
27359             this.shim.remove();
27360         }
27361         this.dd.unreg();
27362         this.proxy.parentNode.removeChild(this.proxy);
27363         if(removeEl){
27364             this.el.remove();
27365         }
27366     }
27367 });
27368
27369 /**
27370  * @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.
27371  */
27372 Roo.SplitBar.createProxy = function(dir){
27373     var proxy = new Roo.Element(document.createElement("div"));
27374     proxy.unselectable();
27375     var cls = 'x-splitbar-proxy';
27376     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27377     document.body.appendChild(proxy.dom);
27378     return proxy.dom;
27379 };
27380
27381 /** 
27382  * @class Roo.SplitBar.BasicLayoutAdapter
27383  * Default Adapter. It assumes the splitter and resizing element are not positioned
27384  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27385  */
27386 Roo.SplitBar.BasicLayoutAdapter = function(){
27387 };
27388
27389 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27390     // do nothing for now
27391     init : function(s){
27392     
27393     },
27394     /**
27395      * Called before drag operations to get the current size of the resizing element. 
27396      * @param {Roo.SplitBar} s The SplitBar using this adapter
27397      */
27398      getElementSize : function(s){
27399         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27400             return s.resizingEl.getWidth();
27401         }else{
27402             return s.resizingEl.getHeight();
27403         }
27404     },
27405     
27406     /**
27407      * Called after drag operations to set the size of the resizing element.
27408      * @param {Roo.SplitBar} s The SplitBar using this adapter
27409      * @param {Number} newSize The new size to set
27410      * @param {Function} onComplete A function to be invoked when resizing is complete
27411      */
27412     setElementSize : function(s, newSize, onComplete){
27413         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27414             if(!s.animate){
27415                 s.resizingEl.setWidth(newSize);
27416                 if(onComplete){
27417                     onComplete(s, newSize);
27418                 }
27419             }else{
27420                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27421             }
27422         }else{
27423             
27424             if(!s.animate){
27425                 s.resizingEl.setHeight(newSize);
27426                 if(onComplete){
27427                     onComplete(s, newSize);
27428                 }
27429             }else{
27430                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27431             }
27432         }
27433     }
27434 };
27435
27436 /** 
27437  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27438  * @extends Roo.SplitBar.BasicLayoutAdapter
27439  * Adapter that  moves the splitter element to align with the resized sizing element. 
27440  * Used with an absolute positioned SplitBar.
27441  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27442  * document.body, make sure you assign an id to the body element.
27443  */
27444 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27445     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27446     this.container = Roo.get(container);
27447 };
27448
27449 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27450     init : function(s){
27451         this.basic.init(s);
27452     },
27453     
27454     getElementSize : function(s){
27455         return this.basic.getElementSize(s);
27456     },
27457     
27458     setElementSize : function(s, newSize, onComplete){
27459         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27460     },
27461     
27462     moveSplitter : function(s){
27463         var yes = Roo.SplitBar;
27464         switch(s.placement){
27465             case yes.LEFT:
27466                 s.el.setX(s.resizingEl.getRight());
27467                 break;
27468             case yes.RIGHT:
27469                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27470                 break;
27471             case yes.TOP:
27472                 s.el.setY(s.resizingEl.getBottom());
27473                 break;
27474             case yes.BOTTOM:
27475                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27476                 break;
27477         }
27478     }
27479 };
27480
27481 /**
27482  * Orientation constant - Create a vertical SplitBar
27483  * @static
27484  * @type Number
27485  */
27486 Roo.SplitBar.VERTICAL = 1;
27487
27488 /**
27489  * Orientation constant - Create a horizontal SplitBar
27490  * @static
27491  * @type Number
27492  */
27493 Roo.SplitBar.HORIZONTAL = 2;
27494
27495 /**
27496  * Placement constant - The resizing element is to the left of the splitter element
27497  * @static
27498  * @type Number
27499  */
27500 Roo.SplitBar.LEFT = 1;
27501
27502 /**
27503  * Placement constant - The resizing element is to the right of the splitter element
27504  * @static
27505  * @type Number
27506  */
27507 Roo.SplitBar.RIGHT = 2;
27508
27509 /**
27510  * Placement constant - The resizing element is positioned above the splitter element
27511  * @static
27512  * @type Number
27513  */
27514 Roo.SplitBar.TOP = 3;
27515
27516 /**
27517  * Placement constant - The resizing element is positioned under splitter element
27518  * @static
27519  * @type Number
27520  */
27521 Roo.SplitBar.BOTTOM = 4;
27522 /*
27523  * Based on:
27524  * Ext JS Library 1.1.1
27525  * Copyright(c) 2006-2007, Ext JS, LLC.
27526  *
27527  * Originally Released Under LGPL - original licence link has changed is not relivant.
27528  *
27529  * Fork - LGPL
27530  * <script type="text/javascript">
27531  */
27532
27533 /**
27534  * @class Roo.View
27535  * @extends Roo.util.Observable
27536  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27537  * This class also supports single and multi selection modes. <br>
27538  * Create a data model bound view:
27539  <pre><code>
27540  var store = new Roo.data.Store(...);
27541
27542  var view = new Roo.View({
27543     el : "my-element",
27544     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27545  
27546     singleSelect: true,
27547     selectedClass: "ydataview-selected",
27548     store: store
27549  });
27550
27551  // listen for node click?
27552  view.on("click", function(vw, index, node, e){
27553  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27554  });
27555
27556  // load XML data
27557  dataModel.load("foobar.xml");
27558  </code></pre>
27559  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27560  * <br><br>
27561  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27562  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27563  * 
27564  * Note: old style constructor is still suported (container, template, config)
27565  * 
27566  * @constructor
27567  * Create a new View
27568  * @param {Object} config The config object
27569  * 
27570  */
27571 Roo.View = function(config, depreciated_tpl, depreciated_config){
27572     
27573     this.parent = false;
27574     
27575     if (typeof(depreciated_tpl) == 'undefined') {
27576         // new way.. - universal constructor.
27577         Roo.apply(this, config);
27578         this.el  = Roo.get(this.el);
27579     } else {
27580         // old format..
27581         this.el  = Roo.get(config);
27582         this.tpl = depreciated_tpl;
27583         Roo.apply(this, depreciated_config);
27584     }
27585     this.wrapEl  = this.el.wrap().wrap();
27586     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27587     
27588     
27589     if(typeof(this.tpl) == "string"){
27590         this.tpl = new Roo.Template(this.tpl);
27591     } else {
27592         // support xtype ctors..
27593         this.tpl = new Roo.factory(this.tpl, Roo);
27594     }
27595     
27596     
27597     this.tpl.compile();
27598     
27599     /** @private */
27600     this.addEvents({
27601         /**
27602          * @event beforeclick
27603          * Fires before a click is processed. Returns false to cancel the default action.
27604          * @param {Roo.View} this
27605          * @param {Number} index The index of the target node
27606          * @param {HTMLElement} node The target node
27607          * @param {Roo.EventObject} e The raw event object
27608          */
27609             "beforeclick" : true,
27610         /**
27611          * @event click
27612          * Fires when a template node is clicked.
27613          * @param {Roo.View} this
27614          * @param {Number} index The index of the target node
27615          * @param {HTMLElement} node The target node
27616          * @param {Roo.EventObject} e The raw event object
27617          */
27618             "click" : true,
27619         /**
27620          * @event dblclick
27621          * Fires when a template node is double clicked.
27622          * @param {Roo.View} this
27623          * @param {Number} index The index of the target node
27624          * @param {HTMLElement} node The target node
27625          * @param {Roo.EventObject} e The raw event object
27626          */
27627             "dblclick" : true,
27628         /**
27629          * @event contextmenu
27630          * Fires when a template node is right clicked.
27631          * @param {Roo.View} this
27632          * @param {Number} index The index of the target node
27633          * @param {HTMLElement} node The target node
27634          * @param {Roo.EventObject} e The raw event object
27635          */
27636             "contextmenu" : true,
27637         /**
27638          * @event selectionchange
27639          * Fires when the selected nodes change.
27640          * @param {Roo.View} this
27641          * @param {Array} selections Array of the selected nodes
27642          */
27643             "selectionchange" : true,
27644     
27645         /**
27646          * @event beforeselect
27647          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27648          * @param {Roo.View} this
27649          * @param {HTMLElement} node The node to be selected
27650          * @param {Array} selections Array of currently selected nodes
27651          */
27652             "beforeselect" : true,
27653         /**
27654          * @event preparedata
27655          * Fires on every row to render, to allow you to change the data.
27656          * @param {Roo.View} this
27657          * @param {Object} data to be rendered (change this)
27658          */
27659           "preparedata" : true
27660           
27661           
27662         });
27663
27664
27665
27666     this.el.on({
27667         "click": this.onClick,
27668         "dblclick": this.onDblClick,
27669         "contextmenu": this.onContextMenu,
27670         scope:this
27671     });
27672
27673     this.selections = [];
27674     this.nodes = [];
27675     this.cmp = new Roo.CompositeElementLite([]);
27676     if(this.store){
27677         this.store = Roo.factory(this.store, Roo.data);
27678         this.setStore(this.store, true);
27679     }
27680     
27681     if ( this.footer && this.footer.xtype) {
27682            
27683          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27684         
27685         this.footer.dataSource = this.store;
27686         this.footer.container = fctr;
27687         this.footer = Roo.factory(this.footer, Roo);
27688         fctr.insertFirst(this.el);
27689         
27690         // this is a bit insane - as the paging toolbar seems to detach the el..
27691 //        dom.parentNode.parentNode.parentNode
27692          // they get detached?
27693     }
27694     
27695     
27696     Roo.View.superclass.constructor.call(this);
27697     
27698     
27699 };
27700
27701 Roo.extend(Roo.View, Roo.util.Observable, {
27702     
27703      /**
27704      * @cfg {Roo.data.Store} store Data store to load data from.
27705      */
27706     store : false,
27707     
27708     /**
27709      * @cfg {String|Roo.Element} el The container element.
27710      */
27711     el : '',
27712     
27713     /**
27714      * @cfg {String|Roo.Template} tpl The template used by this View 
27715      */
27716     tpl : false,
27717     /**
27718      * @cfg {String} dataName the named area of the template to use as the data area
27719      *                          Works with domtemplates roo-name="name"
27720      */
27721     dataName: false,
27722     /**
27723      * @cfg {String} selectedClass The css class to add to selected nodes
27724      */
27725     selectedClass : "x-view-selected",
27726      /**
27727      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27728      */
27729     emptyText : "",
27730     
27731     /**
27732      * @cfg {String} text to display on mask (default Loading)
27733      */
27734     mask : false,
27735     /**
27736      * @cfg {Boolean} multiSelect Allow multiple selection
27737      */
27738     multiSelect : false,
27739     /**
27740      * @cfg {Boolean} singleSelect Allow single selection
27741      */
27742     singleSelect:  false,
27743     
27744     /**
27745      * @cfg {Boolean} toggleSelect - selecting 
27746      */
27747     toggleSelect : false,
27748     
27749     /**
27750      * @cfg {Boolean} tickable - selecting 
27751      */
27752     tickable : false,
27753     
27754     /**
27755      * Returns the element this view is bound to.
27756      * @return {Roo.Element}
27757      */
27758     getEl : function(){
27759         return this.wrapEl;
27760     },
27761     
27762     
27763
27764     /**
27765      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27766      */
27767     refresh : function(){
27768         //Roo.log('refresh');
27769         var t = this.tpl;
27770         
27771         // if we are using something like 'domtemplate', then
27772         // the what gets used is:
27773         // t.applySubtemplate(NAME, data, wrapping data..)
27774         // the outer template then get' applied with
27775         //     the store 'extra data'
27776         // and the body get's added to the
27777         //      roo-name="data" node?
27778         //      <span class='roo-tpl-{name}'></span> ?????
27779         
27780         
27781         
27782         this.clearSelections();
27783         this.el.update("");
27784         var html = [];
27785         var records = this.store.getRange();
27786         if(records.length < 1) {
27787             
27788             // is this valid??  = should it render a template??
27789             
27790             this.el.update(this.emptyText);
27791             return;
27792         }
27793         var el = this.el;
27794         if (this.dataName) {
27795             this.el.update(t.apply(this.store.meta)); //????
27796             el = this.el.child('.roo-tpl-' + this.dataName);
27797         }
27798         
27799         for(var i = 0, len = records.length; i < len; i++){
27800             var data = this.prepareData(records[i].data, i, records[i]);
27801             this.fireEvent("preparedata", this, data, i, records[i]);
27802             
27803             var d = Roo.apply({}, data);
27804             
27805             if(this.tickable){
27806                 Roo.apply(d, {'roo-id' : Roo.id()});
27807                 
27808                 var _this = this;
27809             
27810                 Roo.each(this.parent.item, function(item){
27811                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27812                         return;
27813                     }
27814                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27815                 });
27816             }
27817             
27818             html[html.length] = Roo.util.Format.trim(
27819                 this.dataName ?
27820                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27821                     t.apply(d)
27822             );
27823         }
27824         
27825         
27826         
27827         el.update(html.join(""));
27828         this.nodes = el.dom.childNodes;
27829         this.updateIndexes(0);
27830     },
27831     
27832
27833     /**
27834      * Function to override to reformat the data that is sent to
27835      * the template for each node.
27836      * DEPRICATED - use the preparedata event handler.
27837      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27838      * a JSON object for an UpdateManager bound view).
27839      */
27840     prepareData : function(data, index, record)
27841     {
27842         this.fireEvent("preparedata", this, data, index, record);
27843         return data;
27844     },
27845
27846     onUpdate : function(ds, record){
27847         // Roo.log('on update');   
27848         this.clearSelections();
27849         var index = this.store.indexOf(record);
27850         var n = this.nodes[index];
27851         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27852         n.parentNode.removeChild(n);
27853         this.updateIndexes(index, index);
27854     },
27855
27856     
27857     
27858 // --------- FIXME     
27859     onAdd : function(ds, records, index)
27860     {
27861         //Roo.log(['on Add', ds, records, index] );        
27862         this.clearSelections();
27863         if(this.nodes.length == 0){
27864             this.refresh();
27865             return;
27866         }
27867         var n = this.nodes[index];
27868         for(var i = 0, len = records.length; i < len; i++){
27869             var d = this.prepareData(records[i].data, i, records[i]);
27870             if(n){
27871                 this.tpl.insertBefore(n, d);
27872             }else{
27873                 
27874                 this.tpl.append(this.el, d);
27875             }
27876         }
27877         this.updateIndexes(index);
27878     },
27879
27880     onRemove : function(ds, record, index){
27881        // Roo.log('onRemove');
27882         this.clearSelections();
27883         var el = this.dataName  ?
27884             this.el.child('.roo-tpl-' + this.dataName) :
27885             this.el; 
27886         
27887         el.dom.removeChild(this.nodes[index]);
27888         this.updateIndexes(index);
27889     },
27890
27891     /**
27892      * Refresh an individual node.
27893      * @param {Number} index
27894      */
27895     refreshNode : function(index){
27896         this.onUpdate(this.store, this.store.getAt(index));
27897     },
27898
27899     updateIndexes : function(startIndex, endIndex){
27900         var ns = this.nodes;
27901         startIndex = startIndex || 0;
27902         endIndex = endIndex || ns.length - 1;
27903         for(var i = startIndex; i <= endIndex; i++){
27904             ns[i].nodeIndex = i;
27905         }
27906     },
27907
27908     /**
27909      * Changes the data store this view uses and refresh the view.
27910      * @param {Store} store
27911      */
27912     setStore : function(store, initial){
27913         if(!initial && this.store){
27914             this.store.un("datachanged", this.refresh);
27915             this.store.un("add", this.onAdd);
27916             this.store.un("remove", this.onRemove);
27917             this.store.un("update", this.onUpdate);
27918             this.store.un("clear", this.refresh);
27919             this.store.un("beforeload", this.onBeforeLoad);
27920             this.store.un("load", this.onLoad);
27921             this.store.un("loadexception", this.onLoad);
27922         }
27923         if(store){
27924           
27925             store.on("datachanged", this.refresh, this);
27926             store.on("add", this.onAdd, this);
27927             store.on("remove", this.onRemove, this);
27928             store.on("update", this.onUpdate, this);
27929             store.on("clear", this.refresh, this);
27930             store.on("beforeload", this.onBeforeLoad, this);
27931             store.on("load", this.onLoad, this);
27932             store.on("loadexception", this.onLoad, this);
27933         }
27934         
27935         if(store){
27936             this.refresh();
27937         }
27938     },
27939     /**
27940      * onbeforeLoad - masks the loading area.
27941      *
27942      */
27943     onBeforeLoad : function(store,opts)
27944     {
27945          //Roo.log('onBeforeLoad');   
27946         if (!opts.add) {
27947             this.el.update("");
27948         }
27949         this.el.mask(this.mask ? this.mask : "Loading" ); 
27950     },
27951     onLoad : function ()
27952     {
27953         this.el.unmask();
27954     },
27955     
27956
27957     /**
27958      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27959      * @param {HTMLElement} node
27960      * @return {HTMLElement} The template node
27961      */
27962     findItemFromChild : function(node){
27963         var el = this.dataName  ?
27964             this.el.child('.roo-tpl-' + this.dataName,true) :
27965             this.el.dom; 
27966         
27967         if(!node || node.parentNode == el){
27968                     return node;
27969             }
27970             var p = node.parentNode;
27971             while(p && p != el){
27972             if(p.parentNode == el){
27973                 return p;
27974             }
27975             p = p.parentNode;
27976         }
27977             return null;
27978     },
27979
27980     /** @ignore */
27981     onClick : function(e){
27982         var item = this.findItemFromChild(e.getTarget());
27983         if(item){
27984             var index = this.indexOf(item);
27985             if(this.onItemClick(item, index, e) !== false){
27986                 this.fireEvent("click", this, index, item, e);
27987             }
27988         }else{
27989             this.clearSelections();
27990         }
27991     },
27992
27993     /** @ignore */
27994     onContextMenu : function(e){
27995         var item = this.findItemFromChild(e.getTarget());
27996         if(item){
27997             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27998         }
27999     },
28000
28001     /** @ignore */
28002     onDblClick : function(e){
28003         var item = this.findItemFromChild(e.getTarget());
28004         if(item){
28005             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28006         }
28007     },
28008
28009     onItemClick : function(item, index, e)
28010     {
28011         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28012             return false;
28013         }
28014         if (this.toggleSelect) {
28015             var m = this.isSelected(item) ? 'unselect' : 'select';
28016             //Roo.log(m);
28017             var _t = this;
28018             _t[m](item, true, false);
28019             return true;
28020         }
28021         if(this.multiSelect || this.singleSelect){
28022             if(this.multiSelect && e.shiftKey && this.lastSelection){
28023                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28024             }else{
28025                 this.select(item, this.multiSelect && e.ctrlKey);
28026                 this.lastSelection = item;
28027             }
28028             
28029             if(!this.tickable){
28030                 e.preventDefault();
28031             }
28032             
28033         }
28034         return true;
28035     },
28036
28037     /**
28038      * Get the number of selected nodes.
28039      * @return {Number}
28040      */
28041     getSelectionCount : function(){
28042         return this.selections.length;
28043     },
28044
28045     /**
28046      * Get the currently selected nodes.
28047      * @return {Array} An array of HTMLElements
28048      */
28049     getSelectedNodes : function(){
28050         return this.selections;
28051     },
28052
28053     /**
28054      * Get the indexes of the selected nodes.
28055      * @return {Array}
28056      */
28057     getSelectedIndexes : function(){
28058         var indexes = [], s = this.selections;
28059         for(var i = 0, len = s.length; i < len; i++){
28060             indexes.push(s[i].nodeIndex);
28061         }
28062         return indexes;
28063     },
28064
28065     /**
28066      * Clear all selections
28067      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28068      */
28069     clearSelections : function(suppressEvent){
28070         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28071             this.cmp.elements = this.selections;
28072             this.cmp.removeClass(this.selectedClass);
28073             this.selections = [];
28074             if(!suppressEvent){
28075                 this.fireEvent("selectionchange", this, this.selections);
28076             }
28077         }
28078     },
28079
28080     /**
28081      * Returns true if the passed node is selected
28082      * @param {HTMLElement/Number} node The node or node index
28083      * @return {Boolean}
28084      */
28085     isSelected : function(node){
28086         var s = this.selections;
28087         if(s.length < 1){
28088             return false;
28089         }
28090         node = this.getNode(node);
28091         return s.indexOf(node) !== -1;
28092     },
28093
28094     /**
28095      * Selects nodes.
28096      * @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
28097      * @param {Boolean} keepExisting (optional) true to keep existing selections
28098      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28099      */
28100     select : function(nodeInfo, keepExisting, suppressEvent){
28101         if(nodeInfo instanceof Array){
28102             if(!keepExisting){
28103                 this.clearSelections(true);
28104             }
28105             for(var i = 0, len = nodeInfo.length; i < len; i++){
28106                 this.select(nodeInfo[i], true, true);
28107             }
28108             return;
28109         } 
28110         var node = this.getNode(nodeInfo);
28111         if(!node || this.isSelected(node)){
28112             return; // already selected.
28113         }
28114         if(!keepExisting){
28115             this.clearSelections(true);
28116         }
28117         
28118         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28119             Roo.fly(node).addClass(this.selectedClass);
28120             this.selections.push(node);
28121             if(!suppressEvent){
28122                 this.fireEvent("selectionchange", this, this.selections);
28123             }
28124         }
28125         
28126         
28127     },
28128       /**
28129      * Unselects nodes.
28130      * @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
28131      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28132      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28133      */
28134     unselect : function(nodeInfo, keepExisting, suppressEvent)
28135     {
28136         if(nodeInfo instanceof Array){
28137             Roo.each(this.selections, function(s) {
28138                 this.unselect(s, nodeInfo);
28139             }, this);
28140             return;
28141         }
28142         var node = this.getNode(nodeInfo);
28143         if(!node || !this.isSelected(node)){
28144             //Roo.log("not selected");
28145             return; // not selected.
28146         }
28147         // fireevent???
28148         var ns = [];
28149         Roo.each(this.selections, function(s) {
28150             if (s == node ) {
28151                 Roo.fly(node).removeClass(this.selectedClass);
28152
28153                 return;
28154             }
28155             ns.push(s);
28156         },this);
28157         
28158         this.selections= ns;
28159         this.fireEvent("selectionchange", this, this.selections);
28160     },
28161
28162     /**
28163      * Gets a template node.
28164      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28165      * @return {HTMLElement} The node or null if it wasn't found
28166      */
28167     getNode : function(nodeInfo){
28168         if(typeof nodeInfo == "string"){
28169             return document.getElementById(nodeInfo);
28170         }else if(typeof nodeInfo == "number"){
28171             return this.nodes[nodeInfo];
28172         }
28173         return nodeInfo;
28174     },
28175
28176     /**
28177      * Gets a range template nodes.
28178      * @param {Number} startIndex
28179      * @param {Number} endIndex
28180      * @return {Array} An array of nodes
28181      */
28182     getNodes : function(start, end){
28183         var ns = this.nodes;
28184         start = start || 0;
28185         end = typeof end == "undefined" ? ns.length - 1 : end;
28186         var nodes = [];
28187         if(start <= end){
28188             for(var i = start; i <= end; i++){
28189                 nodes.push(ns[i]);
28190             }
28191         } else{
28192             for(var i = start; i >= end; i--){
28193                 nodes.push(ns[i]);
28194             }
28195         }
28196         return nodes;
28197     },
28198
28199     /**
28200      * Finds the index of the passed node
28201      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28202      * @return {Number} The index of the node or -1
28203      */
28204     indexOf : function(node){
28205         node = this.getNode(node);
28206         if(typeof node.nodeIndex == "number"){
28207             return node.nodeIndex;
28208         }
28209         var ns = this.nodes;
28210         for(var i = 0, len = ns.length; i < len; i++){
28211             if(ns[i] == node){
28212                 return i;
28213             }
28214         }
28215         return -1;
28216     }
28217 });
28218 /*
28219  * Based on:
28220  * Ext JS Library 1.1.1
28221  * Copyright(c) 2006-2007, Ext JS, LLC.
28222  *
28223  * Originally Released Under LGPL - original licence link has changed is not relivant.
28224  *
28225  * Fork - LGPL
28226  * <script type="text/javascript">
28227  */
28228
28229 /**
28230  * @class Roo.JsonView
28231  * @extends Roo.View
28232  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28233 <pre><code>
28234 var view = new Roo.JsonView({
28235     container: "my-element",
28236     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28237     multiSelect: true, 
28238     jsonRoot: "data" 
28239 });
28240
28241 // listen for node click?
28242 view.on("click", function(vw, index, node, e){
28243     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28244 });
28245
28246 // direct load of JSON data
28247 view.load("foobar.php");
28248
28249 // Example from my blog list
28250 var tpl = new Roo.Template(
28251     '&lt;div class="entry"&gt;' +
28252     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28253     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28254     "&lt;/div&gt;&lt;hr /&gt;"
28255 );
28256
28257 var moreView = new Roo.JsonView({
28258     container :  "entry-list", 
28259     template : tpl,
28260     jsonRoot: "posts"
28261 });
28262 moreView.on("beforerender", this.sortEntries, this);
28263 moreView.load({
28264     url: "/blog/get-posts.php",
28265     params: "allposts=true",
28266     text: "Loading Blog Entries..."
28267 });
28268 </code></pre>
28269
28270 * Note: old code is supported with arguments : (container, template, config)
28271
28272
28273  * @constructor
28274  * Create a new JsonView
28275  * 
28276  * @param {Object} config The config object
28277  * 
28278  */
28279 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28280     
28281     
28282     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28283
28284     var um = this.el.getUpdateManager();
28285     um.setRenderer(this);
28286     um.on("update", this.onLoad, this);
28287     um.on("failure", this.onLoadException, this);
28288
28289     /**
28290      * @event beforerender
28291      * Fires before rendering of the downloaded JSON data.
28292      * @param {Roo.JsonView} this
28293      * @param {Object} data The JSON data loaded
28294      */
28295     /**
28296      * @event load
28297      * Fires when data is loaded.
28298      * @param {Roo.JsonView} this
28299      * @param {Object} data The JSON data loaded
28300      * @param {Object} response The raw Connect response object
28301      */
28302     /**
28303      * @event loadexception
28304      * Fires when loading fails.
28305      * @param {Roo.JsonView} this
28306      * @param {Object} response The raw Connect response object
28307      */
28308     this.addEvents({
28309         'beforerender' : true,
28310         'load' : true,
28311         'loadexception' : true
28312     });
28313 };
28314 Roo.extend(Roo.JsonView, Roo.View, {
28315     /**
28316      * @type {String} The root property in the loaded JSON object that contains the data
28317      */
28318     jsonRoot : "",
28319
28320     /**
28321      * Refreshes the view.
28322      */
28323     refresh : function(){
28324         this.clearSelections();
28325         this.el.update("");
28326         var html = [];
28327         var o = this.jsonData;
28328         if(o && o.length > 0){
28329             for(var i = 0, len = o.length; i < len; i++){
28330                 var data = this.prepareData(o[i], i, o);
28331                 html[html.length] = this.tpl.apply(data);
28332             }
28333         }else{
28334             html.push(this.emptyText);
28335         }
28336         this.el.update(html.join(""));
28337         this.nodes = this.el.dom.childNodes;
28338         this.updateIndexes(0);
28339     },
28340
28341     /**
28342      * 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.
28343      * @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:
28344      <pre><code>
28345      view.load({
28346          url: "your-url.php",
28347          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28348          callback: yourFunction,
28349          scope: yourObject, //(optional scope)
28350          discardUrl: false,
28351          nocache: false,
28352          text: "Loading...",
28353          timeout: 30,
28354          scripts: false
28355      });
28356      </code></pre>
28357      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28358      * 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.
28359      * @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}
28360      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28361      * @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.
28362      */
28363     load : function(){
28364         var um = this.el.getUpdateManager();
28365         um.update.apply(um, arguments);
28366     },
28367
28368     // note - render is a standard framework call...
28369     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28370     render : function(el, response){
28371         
28372         this.clearSelections();
28373         this.el.update("");
28374         var o;
28375         try{
28376             if (response != '') {
28377                 o = Roo.util.JSON.decode(response.responseText);
28378                 if(this.jsonRoot){
28379                     
28380                     o = o[this.jsonRoot];
28381                 }
28382             }
28383         } catch(e){
28384         }
28385         /**
28386          * The current JSON data or null
28387          */
28388         this.jsonData = o;
28389         this.beforeRender();
28390         this.refresh();
28391     },
28392
28393 /**
28394  * Get the number of records in the current JSON dataset
28395  * @return {Number}
28396  */
28397     getCount : function(){
28398         return this.jsonData ? this.jsonData.length : 0;
28399     },
28400
28401 /**
28402  * Returns the JSON object for the specified node(s)
28403  * @param {HTMLElement/Array} node The node or an array of nodes
28404  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28405  * you get the JSON object for the node
28406  */
28407     getNodeData : function(node){
28408         if(node instanceof Array){
28409             var data = [];
28410             for(var i = 0, len = node.length; i < len; i++){
28411                 data.push(this.getNodeData(node[i]));
28412             }
28413             return data;
28414         }
28415         return this.jsonData[this.indexOf(node)] || null;
28416     },
28417
28418     beforeRender : function(){
28419         this.snapshot = this.jsonData;
28420         if(this.sortInfo){
28421             this.sort.apply(this, this.sortInfo);
28422         }
28423         this.fireEvent("beforerender", this, this.jsonData);
28424     },
28425
28426     onLoad : function(el, o){
28427         this.fireEvent("load", this, this.jsonData, o);
28428     },
28429
28430     onLoadException : function(el, o){
28431         this.fireEvent("loadexception", this, o);
28432     },
28433
28434 /**
28435  * Filter the data by a specific property.
28436  * @param {String} property A property on your JSON objects
28437  * @param {String/RegExp} value Either string that the property values
28438  * should start with, or a RegExp to test against the property
28439  */
28440     filter : function(property, value){
28441         if(this.jsonData){
28442             var data = [];
28443             var ss = this.snapshot;
28444             if(typeof value == "string"){
28445                 var vlen = value.length;
28446                 if(vlen == 0){
28447                     this.clearFilter();
28448                     return;
28449                 }
28450                 value = value.toLowerCase();
28451                 for(var i = 0, len = ss.length; i < len; i++){
28452                     var o = ss[i];
28453                     if(o[property].substr(0, vlen).toLowerCase() == value){
28454                         data.push(o);
28455                     }
28456                 }
28457             } else if(value.exec){ // regex?
28458                 for(var i = 0, len = ss.length; i < len; i++){
28459                     var o = ss[i];
28460                     if(value.test(o[property])){
28461                         data.push(o);
28462                     }
28463                 }
28464             } else{
28465                 return;
28466             }
28467             this.jsonData = data;
28468             this.refresh();
28469         }
28470     },
28471
28472 /**
28473  * Filter by a function. The passed function will be called with each
28474  * object in the current dataset. If the function returns true the value is kept,
28475  * otherwise it is filtered.
28476  * @param {Function} fn
28477  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28478  */
28479     filterBy : function(fn, scope){
28480         if(this.jsonData){
28481             var data = [];
28482             var ss = this.snapshot;
28483             for(var i = 0, len = ss.length; i < len; i++){
28484                 var o = ss[i];
28485                 if(fn.call(scope || this, o)){
28486                     data.push(o);
28487                 }
28488             }
28489             this.jsonData = data;
28490             this.refresh();
28491         }
28492     },
28493
28494 /**
28495  * Clears the current filter.
28496  */
28497     clearFilter : function(){
28498         if(this.snapshot && this.jsonData != this.snapshot){
28499             this.jsonData = this.snapshot;
28500             this.refresh();
28501         }
28502     },
28503
28504
28505 /**
28506  * Sorts the data for this view and refreshes it.
28507  * @param {String} property A property on your JSON objects to sort on
28508  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28509  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28510  */
28511     sort : function(property, dir, sortType){
28512         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28513         if(this.jsonData){
28514             var p = property;
28515             var dsc = dir && dir.toLowerCase() == "desc";
28516             var f = function(o1, o2){
28517                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28518                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28519                 ;
28520                 if(v1 < v2){
28521                     return dsc ? +1 : -1;
28522                 } else if(v1 > v2){
28523                     return dsc ? -1 : +1;
28524                 } else{
28525                     return 0;
28526                 }
28527             };
28528             this.jsonData.sort(f);
28529             this.refresh();
28530             if(this.jsonData != this.snapshot){
28531                 this.snapshot.sort(f);
28532             }
28533         }
28534     }
28535 });/*
28536  * Based on:
28537  * Ext JS Library 1.1.1
28538  * Copyright(c) 2006-2007, Ext JS, LLC.
28539  *
28540  * Originally Released Under LGPL - original licence link has changed is not relivant.
28541  *
28542  * Fork - LGPL
28543  * <script type="text/javascript">
28544  */
28545  
28546
28547 /**
28548  * @class Roo.ColorPalette
28549  * @extends Roo.Component
28550  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28551  * Here's an example of typical usage:
28552  * <pre><code>
28553 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28554 cp.render('my-div');
28555
28556 cp.on('select', function(palette, selColor){
28557     // do something with selColor
28558 });
28559 </code></pre>
28560  * @constructor
28561  * Create a new ColorPalette
28562  * @param {Object} config The config object
28563  */
28564 Roo.ColorPalette = function(config){
28565     Roo.ColorPalette.superclass.constructor.call(this, config);
28566     this.addEvents({
28567         /**
28568              * @event select
28569              * Fires when a color is selected
28570              * @param {ColorPalette} this
28571              * @param {String} color The 6-digit color hex code (without the # symbol)
28572              */
28573         select: true
28574     });
28575
28576     if(this.handler){
28577         this.on("select", this.handler, this.scope, true);
28578     }
28579 };
28580 Roo.extend(Roo.ColorPalette, Roo.Component, {
28581     /**
28582      * @cfg {String} itemCls
28583      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28584      */
28585     itemCls : "x-color-palette",
28586     /**
28587      * @cfg {String} value
28588      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28589      * the hex codes are case-sensitive.
28590      */
28591     value : null,
28592     clickEvent:'click',
28593     // private
28594     ctype: "Roo.ColorPalette",
28595
28596     /**
28597      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28598      */
28599     allowReselect : false,
28600
28601     /**
28602      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28603      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28604      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28605      * of colors with the width setting until the box is symmetrical.</p>
28606      * <p>You can override individual colors if needed:</p>
28607      * <pre><code>
28608 var cp = new Roo.ColorPalette();
28609 cp.colors[0] = "FF0000";  // change the first box to red
28610 </code></pre>
28611
28612 Or you can provide a custom array of your own for complete control:
28613 <pre><code>
28614 var cp = new Roo.ColorPalette();
28615 cp.colors = ["000000", "993300", "333300"];
28616 </code></pre>
28617      * @type Array
28618      */
28619     colors : [
28620         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28621         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28622         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28623         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28624         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28625     ],
28626
28627     // private
28628     onRender : function(container, position){
28629         var t = new Roo.MasterTemplate(
28630             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28631         );
28632         var c = this.colors;
28633         for(var i = 0, len = c.length; i < len; i++){
28634             t.add([c[i]]);
28635         }
28636         var el = document.createElement("div");
28637         el.className = this.itemCls;
28638         t.overwrite(el);
28639         container.dom.insertBefore(el, position);
28640         this.el = Roo.get(el);
28641         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28642         if(this.clickEvent != 'click'){
28643             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28644         }
28645     },
28646
28647     // private
28648     afterRender : function(){
28649         Roo.ColorPalette.superclass.afterRender.call(this);
28650         if(this.value){
28651             var s = this.value;
28652             this.value = null;
28653             this.select(s);
28654         }
28655     },
28656
28657     // private
28658     handleClick : function(e, t){
28659         e.preventDefault();
28660         if(!this.disabled){
28661             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28662             this.select(c.toUpperCase());
28663         }
28664     },
28665
28666     /**
28667      * Selects the specified color in the palette (fires the select event)
28668      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28669      */
28670     select : function(color){
28671         color = color.replace("#", "");
28672         if(color != this.value || this.allowReselect){
28673             var el = this.el;
28674             if(this.value){
28675                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28676             }
28677             el.child("a.color-"+color).addClass("x-color-palette-sel");
28678             this.value = color;
28679             this.fireEvent("select", this, color);
28680         }
28681     }
28682 });/*
28683  * Based on:
28684  * Ext JS Library 1.1.1
28685  * Copyright(c) 2006-2007, Ext JS, LLC.
28686  *
28687  * Originally Released Under LGPL - original licence link has changed is not relivant.
28688  *
28689  * Fork - LGPL
28690  * <script type="text/javascript">
28691  */
28692  
28693 /**
28694  * @class Roo.DatePicker
28695  * @extends Roo.Component
28696  * Simple date picker class.
28697  * @constructor
28698  * Create a new DatePicker
28699  * @param {Object} config The config object
28700  */
28701 Roo.DatePicker = function(config){
28702     Roo.DatePicker.superclass.constructor.call(this, config);
28703
28704     this.value = config && config.value ?
28705                  config.value.clearTime() : new Date().clearTime();
28706
28707     this.addEvents({
28708         /**
28709              * @event select
28710              * Fires when a date is selected
28711              * @param {DatePicker} this
28712              * @param {Date} date The selected date
28713              */
28714         'select': true,
28715         /**
28716              * @event monthchange
28717              * Fires when the displayed month changes 
28718              * @param {DatePicker} this
28719              * @param {Date} date The selected month
28720              */
28721         'monthchange': true
28722     });
28723
28724     if(this.handler){
28725         this.on("select", this.handler,  this.scope || this);
28726     }
28727     // build the disabledDatesRE
28728     if(!this.disabledDatesRE && this.disabledDates){
28729         var dd = this.disabledDates;
28730         var re = "(?:";
28731         for(var i = 0; i < dd.length; i++){
28732             re += dd[i];
28733             if(i != dd.length-1) {
28734                 re += "|";
28735             }
28736         }
28737         this.disabledDatesRE = new RegExp(re + ")");
28738     }
28739 };
28740
28741 Roo.extend(Roo.DatePicker, Roo.Component, {
28742     /**
28743      * @cfg {String} todayText
28744      * The text to display on the button that selects the current date (defaults to "Today")
28745      */
28746     todayText : "Today",
28747     /**
28748      * @cfg {String} okText
28749      * The text to display on the ok button
28750      */
28751     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28752     /**
28753      * @cfg {String} cancelText
28754      * The text to display on the cancel button
28755      */
28756     cancelText : "Cancel",
28757     /**
28758      * @cfg {String} todayTip
28759      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28760      */
28761     todayTip : "{0} (Spacebar)",
28762     /**
28763      * @cfg {Date} minDate
28764      * Minimum allowable date (JavaScript date object, defaults to null)
28765      */
28766     minDate : null,
28767     /**
28768      * @cfg {Date} maxDate
28769      * Maximum allowable date (JavaScript date object, defaults to null)
28770      */
28771     maxDate : null,
28772     /**
28773      * @cfg {String} minText
28774      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28775      */
28776     minText : "This date is before the minimum date",
28777     /**
28778      * @cfg {String} maxText
28779      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28780      */
28781     maxText : "This date is after the maximum date",
28782     /**
28783      * @cfg {String} format
28784      * The default date format string which can be overriden for localization support.  The format must be
28785      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28786      */
28787     format : "m/d/y",
28788     /**
28789      * @cfg {Array} disabledDays
28790      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28791      */
28792     disabledDays : null,
28793     /**
28794      * @cfg {String} disabledDaysText
28795      * The tooltip to display when the date falls on a disabled day (defaults to "")
28796      */
28797     disabledDaysText : "",
28798     /**
28799      * @cfg {RegExp} disabledDatesRE
28800      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28801      */
28802     disabledDatesRE : null,
28803     /**
28804      * @cfg {String} disabledDatesText
28805      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28806      */
28807     disabledDatesText : "",
28808     /**
28809      * @cfg {Boolean} constrainToViewport
28810      * True to constrain the date picker to the viewport (defaults to true)
28811      */
28812     constrainToViewport : true,
28813     /**
28814      * @cfg {Array} monthNames
28815      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28816      */
28817     monthNames : Date.monthNames,
28818     /**
28819      * @cfg {Array} dayNames
28820      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28821      */
28822     dayNames : Date.dayNames,
28823     /**
28824      * @cfg {String} nextText
28825      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28826      */
28827     nextText: 'Next Month (Control+Right)',
28828     /**
28829      * @cfg {String} prevText
28830      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28831      */
28832     prevText: 'Previous Month (Control+Left)',
28833     /**
28834      * @cfg {String} monthYearText
28835      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28836      */
28837     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28838     /**
28839      * @cfg {Number} startDay
28840      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28841      */
28842     startDay : 0,
28843     /**
28844      * @cfg {Bool} showClear
28845      * Show a clear button (usefull for date form elements that can be blank.)
28846      */
28847     
28848     showClear: false,
28849     
28850     /**
28851      * Sets the value of the date field
28852      * @param {Date} value The date to set
28853      */
28854     setValue : function(value){
28855         var old = this.value;
28856         
28857         if (typeof(value) == 'string') {
28858          
28859             value = Date.parseDate(value, this.format);
28860         }
28861         if (!value) {
28862             value = new Date();
28863         }
28864         
28865         this.value = value.clearTime(true);
28866         if(this.el){
28867             this.update(this.value);
28868         }
28869     },
28870
28871     /**
28872      * Gets the current selected value of the date field
28873      * @return {Date} The selected date
28874      */
28875     getValue : function(){
28876         return this.value;
28877     },
28878
28879     // private
28880     focus : function(){
28881         if(this.el){
28882             this.update(this.activeDate);
28883         }
28884     },
28885
28886     // privateval
28887     onRender : function(container, position){
28888         
28889         var m = [
28890              '<table cellspacing="0">',
28891                 '<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>',
28892                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28893         var dn = this.dayNames;
28894         for(var i = 0; i < 7; i++){
28895             var d = this.startDay+i;
28896             if(d > 6){
28897                 d = d-7;
28898             }
28899             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28900         }
28901         m[m.length] = "</tr></thead><tbody><tr>";
28902         for(var i = 0; i < 42; i++) {
28903             if(i % 7 == 0 && i != 0){
28904                 m[m.length] = "</tr><tr>";
28905             }
28906             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28907         }
28908         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28909             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28910
28911         var el = document.createElement("div");
28912         el.className = "x-date-picker";
28913         el.innerHTML = m.join("");
28914
28915         container.dom.insertBefore(el, position);
28916
28917         this.el = Roo.get(el);
28918         this.eventEl = Roo.get(el.firstChild);
28919
28920         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28921             handler: this.showPrevMonth,
28922             scope: this,
28923             preventDefault:true,
28924             stopDefault:true
28925         });
28926
28927         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28928             handler: this.showNextMonth,
28929             scope: this,
28930             preventDefault:true,
28931             stopDefault:true
28932         });
28933
28934         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28935
28936         this.monthPicker = this.el.down('div.x-date-mp');
28937         this.monthPicker.enableDisplayMode('block');
28938         
28939         var kn = new Roo.KeyNav(this.eventEl, {
28940             "left" : function(e){
28941                 e.ctrlKey ?
28942                     this.showPrevMonth() :
28943                     this.update(this.activeDate.add("d", -1));
28944             },
28945
28946             "right" : function(e){
28947                 e.ctrlKey ?
28948                     this.showNextMonth() :
28949                     this.update(this.activeDate.add("d", 1));
28950             },
28951
28952             "up" : function(e){
28953                 e.ctrlKey ?
28954                     this.showNextYear() :
28955                     this.update(this.activeDate.add("d", -7));
28956             },
28957
28958             "down" : function(e){
28959                 e.ctrlKey ?
28960                     this.showPrevYear() :
28961                     this.update(this.activeDate.add("d", 7));
28962             },
28963
28964             "pageUp" : function(e){
28965                 this.showNextMonth();
28966             },
28967
28968             "pageDown" : function(e){
28969                 this.showPrevMonth();
28970             },
28971
28972             "enter" : function(e){
28973                 e.stopPropagation();
28974                 return true;
28975             },
28976
28977             scope : this
28978         });
28979
28980         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28981
28982         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28983
28984         this.el.unselectable();
28985         
28986         this.cells = this.el.select("table.x-date-inner tbody td");
28987         this.textNodes = this.el.query("table.x-date-inner tbody span");
28988
28989         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28990             text: "&#160;",
28991             tooltip: this.monthYearText
28992         });
28993
28994         this.mbtn.on('click', this.showMonthPicker, this);
28995         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28996
28997
28998         var today = (new Date()).dateFormat(this.format);
28999         
29000         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29001         if (this.showClear) {
29002             baseTb.add( new Roo.Toolbar.Fill());
29003         }
29004         baseTb.add({
29005             text: String.format(this.todayText, today),
29006             tooltip: String.format(this.todayTip, today),
29007             handler: this.selectToday,
29008             scope: this
29009         });
29010         
29011         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29012             
29013         //});
29014         if (this.showClear) {
29015             
29016             baseTb.add( new Roo.Toolbar.Fill());
29017             baseTb.add({
29018                 text: '&#160;',
29019                 cls: 'x-btn-icon x-btn-clear',
29020                 handler: function() {
29021                     //this.value = '';
29022                     this.fireEvent("select", this, '');
29023                 },
29024                 scope: this
29025             });
29026         }
29027         
29028         
29029         if(Roo.isIE){
29030             this.el.repaint();
29031         }
29032         this.update(this.value);
29033     },
29034
29035     createMonthPicker : function(){
29036         if(!this.monthPicker.dom.firstChild){
29037             var buf = ['<table border="0" cellspacing="0">'];
29038             for(var i = 0; i < 6; i++){
29039                 buf.push(
29040                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29041                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29042                     i == 0 ?
29043                     '<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>' :
29044                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29045                 );
29046             }
29047             buf.push(
29048                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29049                     this.okText,
29050                     '</button><button type="button" class="x-date-mp-cancel">',
29051                     this.cancelText,
29052                     '</button></td></tr>',
29053                 '</table>'
29054             );
29055             this.monthPicker.update(buf.join(''));
29056             this.monthPicker.on('click', this.onMonthClick, this);
29057             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29058
29059             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29060             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29061
29062             this.mpMonths.each(function(m, a, i){
29063                 i += 1;
29064                 if((i%2) == 0){
29065                     m.dom.xmonth = 5 + Math.round(i * .5);
29066                 }else{
29067                     m.dom.xmonth = Math.round((i-1) * .5);
29068                 }
29069             });
29070         }
29071     },
29072
29073     showMonthPicker : function(){
29074         this.createMonthPicker();
29075         var size = this.el.getSize();
29076         this.monthPicker.setSize(size);
29077         this.monthPicker.child('table').setSize(size);
29078
29079         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29080         this.updateMPMonth(this.mpSelMonth);
29081         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29082         this.updateMPYear(this.mpSelYear);
29083
29084         this.monthPicker.slideIn('t', {duration:.2});
29085     },
29086
29087     updateMPYear : function(y){
29088         this.mpyear = y;
29089         var ys = this.mpYears.elements;
29090         for(var i = 1; i <= 10; i++){
29091             var td = ys[i-1], y2;
29092             if((i%2) == 0){
29093                 y2 = y + Math.round(i * .5);
29094                 td.firstChild.innerHTML = y2;
29095                 td.xyear = y2;
29096             }else{
29097                 y2 = y - (5-Math.round(i * .5));
29098                 td.firstChild.innerHTML = y2;
29099                 td.xyear = y2;
29100             }
29101             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29102         }
29103     },
29104
29105     updateMPMonth : function(sm){
29106         this.mpMonths.each(function(m, a, i){
29107             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29108         });
29109     },
29110
29111     selectMPMonth: function(m){
29112         
29113     },
29114
29115     onMonthClick : function(e, t){
29116         e.stopEvent();
29117         var el = new Roo.Element(t), pn;
29118         if(el.is('button.x-date-mp-cancel')){
29119             this.hideMonthPicker();
29120         }
29121         else if(el.is('button.x-date-mp-ok')){
29122             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29123             this.hideMonthPicker();
29124         }
29125         else if(pn = el.up('td.x-date-mp-month', 2)){
29126             this.mpMonths.removeClass('x-date-mp-sel');
29127             pn.addClass('x-date-mp-sel');
29128             this.mpSelMonth = pn.dom.xmonth;
29129         }
29130         else if(pn = el.up('td.x-date-mp-year', 2)){
29131             this.mpYears.removeClass('x-date-mp-sel');
29132             pn.addClass('x-date-mp-sel');
29133             this.mpSelYear = pn.dom.xyear;
29134         }
29135         else if(el.is('a.x-date-mp-prev')){
29136             this.updateMPYear(this.mpyear-10);
29137         }
29138         else if(el.is('a.x-date-mp-next')){
29139             this.updateMPYear(this.mpyear+10);
29140         }
29141     },
29142
29143     onMonthDblClick : function(e, t){
29144         e.stopEvent();
29145         var el = new Roo.Element(t), pn;
29146         if(pn = el.up('td.x-date-mp-month', 2)){
29147             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29148             this.hideMonthPicker();
29149         }
29150         else if(pn = el.up('td.x-date-mp-year', 2)){
29151             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29152             this.hideMonthPicker();
29153         }
29154     },
29155
29156     hideMonthPicker : function(disableAnim){
29157         if(this.monthPicker){
29158             if(disableAnim === true){
29159                 this.monthPicker.hide();
29160             }else{
29161                 this.monthPicker.slideOut('t', {duration:.2});
29162             }
29163         }
29164     },
29165
29166     // private
29167     showPrevMonth : function(e){
29168         this.update(this.activeDate.add("mo", -1));
29169     },
29170
29171     // private
29172     showNextMonth : function(e){
29173         this.update(this.activeDate.add("mo", 1));
29174     },
29175
29176     // private
29177     showPrevYear : function(){
29178         this.update(this.activeDate.add("y", -1));
29179     },
29180
29181     // private
29182     showNextYear : function(){
29183         this.update(this.activeDate.add("y", 1));
29184     },
29185
29186     // private
29187     handleMouseWheel : function(e){
29188         var delta = e.getWheelDelta();
29189         if(delta > 0){
29190             this.showPrevMonth();
29191             e.stopEvent();
29192         } else if(delta < 0){
29193             this.showNextMonth();
29194             e.stopEvent();
29195         }
29196     },
29197
29198     // private
29199     handleDateClick : function(e, t){
29200         e.stopEvent();
29201         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29202             this.setValue(new Date(t.dateValue));
29203             this.fireEvent("select", this, this.value);
29204         }
29205     },
29206
29207     // private
29208     selectToday : function(){
29209         this.setValue(new Date().clearTime());
29210         this.fireEvent("select", this, this.value);
29211     },
29212
29213     // private
29214     update : function(date)
29215     {
29216         var vd = this.activeDate;
29217         this.activeDate = date;
29218         if(vd && this.el){
29219             var t = date.getTime();
29220             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29221                 this.cells.removeClass("x-date-selected");
29222                 this.cells.each(function(c){
29223                    if(c.dom.firstChild.dateValue == t){
29224                        c.addClass("x-date-selected");
29225                        setTimeout(function(){
29226                             try{c.dom.firstChild.focus();}catch(e){}
29227                        }, 50);
29228                        return false;
29229                    }
29230                 });
29231                 return;
29232             }
29233         }
29234         
29235         var days = date.getDaysInMonth();
29236         var firstOfMonth = date.getFirstDateOfMonth();
29237         var startingPos = firstOfMonth.getDay()-this.startDay;
29238
29239         if(startingPos <= this.startDay){
29240             startingPos += 7;
29241         }
29242
29243         var pm = date.add("mo", -1);
29244         var prevStart = pm.getDaysInMonth()-startingPos;
29245
29246         var cells = this.cells.elements;
29247         var textEls = this.textNodes;
29248         days += startingPos;
29249
29250         // convert everything to numbers so it's fast
29251         var day = 86400000;
29252         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29253         var today = new Date().clearTime().getTime();
29254         var sel = date.clearTime().getTime();
29255         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29256         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29257         var ddMatch = this.disabledDatesRE;
29258         var ddText = this.disabledDatesText;
29259         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29260         var ddaysText = this.disabledDaysText;
29261         var format = this.format;
29262
29263         var setCellClass = function(cal, cell){
29264             cell.title = "";
29265             var t = d.getTime();
29266             cell.firstChild.dateValue = t;
29267             if(t == today){
29268                 cell.className += " x-date-today";
29269                 cell.title = cal.todayText;
29270             }
29271             if(t == sel){
29272                 cell.className += " x-date-selected";
29273                 setTimeout(function(){
29274                     try{cell.firstChild.focus();}catch(e){}
29275                 }, 50);
29276             }
29277             // disabling
29278             if(t < min) {
29279                 cell.className = " x-date-disabled";
29280                 cell.title = cal.minText;
29281                 return;
29282             }
29283             if(t > max) {
29284                 cell.className = " x-date-disabled";
29285                 cell.title = cal.maxText;
29286                 return;
29287             }
29288             if(ddays){
29289                 if(ddays.indexOf(d.getDay()) != -1){
29290                     cell.title = ddaysText;
29291                     cell.className = " x-date-disabled";
29292                 }
29293             }
29294             if(ddMatch && format){
29295                 var fvalue = d.dateFormat(format);
29296                 if(ddMatch.test(fvalue)){
29297                     cell.title = ddText.replace("%0", fvalue);
29298                     cell.className = " x-date-disabled";
29299                 }
29300             }
29301         };
29302
29303         var i = 0;
29304         for(; i < startingPos; i++) {
29305             textEls[i].innerHTML = (++prevStart);
29306             d.setDate(d.getDate()+1);
29307             cells[i].className = "x-date-prevday";
29308             setCellClass(this, cells[i]);
29309         }
29310         for(; i < days; i++){
29311             intDay = i - startingPos + 1;
29312             textEls[i].innerHTML = (intDay);
29313             d.setDate(d.getDate()+1);
29314             cells[i].className = "x-date-active";
29315             setCellClass(this, cells[i]);
29316         }
29317         var extraDays = 0;
29318         for(; i < 42; i++) {
29319              textEls[i].innerHTML = (++extraDays);
29320              d.setDate(d.getDate()+1);
29321              cells[i].className = "x-date-nextday";
29322              setCellClass(this, cells[i]);
29323         }
29324
29325         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29326         this.fireEvent('monthchange', this, date);
29327         
29328         if(!this.internalRender){
29329             var main = this.el.dom.firstChild;
29330             var w = main.offsetWidth;
29331             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29332             Roo.fly(main).setWidth(w);
29333             this.internalRender = true;
29334             // opera does not respect the auto grow header center column
29335             // then, after it gets a width opera refuses to recalculate
29336             // without a second pass
29337             if(Roo.isOpera && !this.secondPass){
29338                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29339                 this.secondPass = true;
29340                 this.update.defer(10, this, [date]);
29341             }
29342         }
29343         
29344         
29345     }
29346 });        /*
29347  * Based on:
29348  * Ext JS Library 1.1.1
29349  * Copyright(c) 2006-2007, Ext JS, LLC.
29350  *
29351  * Originally Released Under LGPL - original licence link has changed is not relivant.
29352  *
29353  * Fork - LGPL
29354  * <script type="text/javascript">
29355  */
29356 /**
29357  * @class Roo.TabPanel
29358  * @extends Roo.util.Observable
29359  * A lightweight tab container.
29360  * <br><br>
29361  * Usage:
29362  * <pre><code>
29363 // basic tabs 1, built from existing content
29364 var tabs = new Roo.TabPanel("tabs1");
29365 tabs.addTab("script", "View Script");
29366 tabs.addTab("markup", "View Markup");
29367 tabs.activate("script");
29368
29369 // more advanced tabs, built from javascript
29370 var jtabs = new Roo.TabPanel("jtabs");
29371 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29372
29373 // set up the UpdateManager
29374 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29375 var updater = tab2.getUpdateManager();
29376 updater.setDefaultUrl("ajax1.htm");
29377 tab2.on('activate', updater.refresh, updater, true);
29378
29379 // Use setUrl for Ajax loading
29380 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29381 tab3.setUrl("ajax2.htm", null, true);
29382
29383 // Disabled tab
29384 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29385 tab4.disable();
29386
29387 jtabs.activate("jtabs-1");
29388  * </code></pre>
29389  * @constructor
29390  * Create a new TabPanel.
29391  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29392  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29393  */
29394 Roo.TabPanel = function(container, config){
29395     /**
29396     * The container element for this TabPanel.
29397     * @type Roo.Element
29398     */
29399     this.el = Roo.get(container, true);
29400     if(config){
29401         if(typeof config == "boolean"){
29402             this.tabPosition = config ? "bottom" : "top";
29403         }else{
29404             Roo.apply(this, config);
29405         }
29406     }
29407     if(this.tabPosition == "bottom"){
29408         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29409         this.el.addClass("x-tabs-bottom");
29410     }
29411     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29412     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29413     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29414     if(Roo.isIE){
29415         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29416     }
29417     if(this.tabPosition != "bottom"){
29418         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29419          * @type Roo.Element
29420          */
29421         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29422         this.el.addClass("x-tabs-top");
29423     }
29424     this.items = [];
29425
29426     this.bodyEl.setStyle("position", "relative");
29427
29428     this.active = null;
29429     this.activateDelegate = this.activate.createDelegate(this);
29430
29431     this.addEvents({
29432         /**
29433          * @event tabchange
29434          * Fires when the active tab changes
29435          * @param {Roo.TabPanel} this
29436          * @param {Roo.TabPanelItem} activePanel The new active tab
29437          */
29438         "tabchange": true,
29439         /**
29440          * @event beforetabchange
29441          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29442          * @param {Roo.TabPanel} this
29443          * @param {Object} e Set cancel to true on this object to cancel the tab change
29444          * @param {Roo.TabPanelItem} tab The tab being changed to
29445          */
29446         "beforetabchange" : true
29447     });
29448
29449     Roo.EventManager.onWindowResize(this.onResize, this);
29450     this.cpad = this.el.getPadding("lr");
29451     this.hiddenCount = 0;
29452
29453
29454     // toolbar on the tabbar support...
29455     if (this.toolbar) {
29456         var tcfg = this.toolbar;
29457         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29458         this.toolbar = new Roo.Toolbar(tcfg);
29459         if (Roo.isSafari) {
29460             var tbl = tcfg.container.child('table', true);
29461             tbl.setAttribute('width', '100%');
29462         }
29463         
29464     }
29465    
29466
29467
29468     Roo.TabPanel.superclass.constructor.call(this);
29469 };
29470
29471 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29472     /*
29473      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29474      */
29475     tabPosition : "top",
29476     /*
29477      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29478      */
29479     currentTabWidth : 0,
29480     /*
29481      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29482      */
29483     minTabWidth : 40,
29484     /*
29485      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29486      */
29487     maxTabWidth : 250,
29488     /*
29489      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29490      */
29491     preferredTabWidth : 175,
29492     /*
29493      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29494      */
29495     resizeTabs : false,
29496     /*
29497      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29498      */
29499     monitorResize : true,
29500     /*
29501      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29502      */
29503     toolbar : false,
29504
29505     /**
29506      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29507      * @param {String} id The id of the div to use <b>or create</b>
29508      * @param {String} text The text for the tab
29509      * @param {String} content (optional) Content to put in the TabPanelItem body
29510      * @param {Boolean} closable (optional) True to create a close icon on the tab
29511      * @return {Roo.TabPanelItem} The created TabPanelItem
29512      */
29513     addTab : function(id, text, content, closable){
29514         var item = new Roo.TabPanelItem(this, id, text, closable);
29515         this.addTabItem(item);
29516         if(content){
29517             item.setContent(content);
29518         }
29519         return item;
29520     },
29521
29522     /**
29523      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29524      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29525      * @return {Roo.TabPanelItem}
29526      */
29527     getTab : function(id){
29528         return this.items[id];
29529     },
29530
29531     /**
29532      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29533      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29534      */
29535     hideTab : function(id){
29536         var t = this.items[id];
29537         if(!t.isHidden()){
29538            t.setHidden(true);
29539            this.hiddenCount++;
29540            this.autoSizeTabs();
29541         }
29542     },
29543
29544     /**
29545      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29546      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29547      */
29548     unhideTab : function(id){
29549         var t = this.items[id];
29550         if(t.isHidden()){
29551            t.setHidden(false);
29552            this.hiddenCount--;
29553            this.autoSizeTabs();
29554         }
29555     },
29556
29557     /**
29558      * Adds an existing {@link Roo.TabPanelItem}.
29559      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29560      */
29561     addTabItem : function(item){
29562         this.items[item.id] = item;
29563         this.items.push(item);
29564         if(this.resizeTabs){
29565            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29566            this.autoSizeTabs();
29567         }else{
29568             item.autoSize();
29569         }
29570     },
29571
29572     /**
29573      * Removes a {@link Roo.TabPanelItem}.
29574      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29575      */
29576     removeTab : function(id){
29577         var items = this.items;
29578         var tab = items[id];
29579         if(!tab) { return; }
29580         var index = items.indexOf(tab);
29581         if(this.active == tab && items.length > 1){
29582             var newTab = this.getNextAvailable(index);
29583             if(newTab) {
29584                 newTab.activate();
29585             }
29586         }
29587         this.stripEl.dom.removeChild(tab.pnode.dom);
29588         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29589             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29590         }
29591         items.splice(index, 1);
29592         delete this.items[tab.id];
29593         tab.fireEvent("close", tab);
29594         tab.purgeListeners();
29595         this.autoSizeTabs();
29596     },
29597
29598     getNextAvailable : function(start){
29599         var items = this.items;
29600         var index = start;
29601         // look for a next tab that will slide over to
29602         // replace the one being removed
29603         while(index < items.length){
29604             var item = items[++index];
29605             if(item && !item.isHidden()){
29606                 return item;
29607             }
29608         }
29609         // if one isn't found select the previous tab (on the left)
29610         index = start;
29611         while(index >= 0){
29612             var item = items[--index];
29613             if(item && !item.isHidden()){
29614                 return item;
29615             }
29616         }
29617         return null;
29618     },
29619
29620     /**
29621      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29622      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29623      */
29624     disableTab : function(id){
29625         var tab = this.items[id];
29626         if(tab && this.active != tab){
29627             tab.disable();
29628         }
29629     },
29630
29631     /**
29632      * Enables a {@link Roo.TabPanelItem} that is disabled.
29633      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29634      */
29635     enableTab : function(id){
29636         var tab = this.items[id];
29637         tab.enable();
29638     },
29639
29640     /**
29641      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29642      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29643      * @return {Roo.TabPanelItem} The TabPanelItem.
29644      */
29645     activate : function(id){
29646         var tab = this.items[id];
29647         if(!tab){
29648             return null;
29649         }
29650         if(tab == this.active || tab.disabled){
29651             return tab;
29652         }
29653         var e = {};
29654         this.fireEvent("beforetabchange", this, e, tab);
29655         if(e.cancel !== true && !tab.disabled){
29656             if(this.active){
29657                 this.active.hide();
29658             }
29659             this.active = this.items[id];
29660             this.active.show();
29661             this.fireEvent("tabchange", this, this.active);
29662         }
29663         return tab;
29664     },
29665
29666     /**
29667      * Gets the active {@link Roo.TabPanelItem}.
29668      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29669      */
29670     getActiveTab : function(){
29671         return this.active;
29672     },
29673
29674     /**
29675      * Updates the tab body element to fit the height of the container element
29676      * for overflow scrolling
29677      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29678      */
29679     syncHeight : function(targetHeight){
29680         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29681         var bm = this.bodyEl.getMargins();
29682         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29683         this.bodyEl.setHeight(newHeight);
29684         return newHeight;
29685     },
29686
29687     onResize : function(){
29688         if(this.monitorResize){
29689             this.autoSizeTabs();
29690         }
29691     },
29692
29693     /**
29694      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29695      */
29696     beginUpdate : function(){
29697         this.updating = true;
29698     },
29699
29700     /**
29701      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29702      */
29703     endUpdate : function(){
29704         this.updating = false;
29705         this.autoSizeTabs();
29706     },
29707
29708     /**
29709      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29710      */
29711     autoSizeTabs : function(){
29712         var count = this.items.length;
29713         var vcount = count - this.hiddenCount;
29714         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29715             return;
29716         }
29717         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29718         var availWidth = Math.floor(w / vcount);
29719         var b = this.stripBody;
29720         if(b.getWidth() > w){
29721             var tabs = this.items;
29722             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29723             if(availWidth < this.minTabWidth){
29724                 /*if(!this.sleft){    // incomplete scrolling code
29725                     this.createScrollButtons();
29726                 }
29727                 this.showScroll();
29728                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29729             }
29730         }else{
29731             if(this.currentTabWidth < this.preferredTabWidth){
29732                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29733             }
29734         }
29735     },
29736
29737     /**
29738      * Returns the number of tabs in this TabPanel.
29739      * @return {Number}
29740      */
29741      getCount : function(){
29742          return this.items.length;
29743      },
29744
29745     /**
29746      * Resizes all the tabs to the passed width
29747      * @param {Number} The new width
29748      */
29749     setTabWidth : function(width){
29750         this.currentTabWidth = width;
29751         for(var i = 0, len = this.items.length; i < len; i++) {
29752                 if(!this.items[i].isHidden()) {
29753                 this.items[i].setWidth(width);
29754             }
29755         }
29756     },
29757
29758     /**
29759      * Destroys this TabPanel
29760      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29761      */
29762     destroy : function(removeEl){
29763         Roo.EventManager.removeResizeListener(this.onResize, this);
29764         for(var i = 0, len = this.items.length; i < len; i++){
29765             this.items[i].purgeListeners();
29766         }
29767         if(removeEl === true){
29768             this.el.update("");
29769             this.el.remove();
29770         }
29771     }
29772 });
29773
29774 /**
29775  * @class Roo.TabPanelItem
29776  * @extends Roo.util.Observable
29777  * Represents an individual item (tab plus body) in a TabPanel.
29778  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29779  * @param {String} id The id of this TabPanelItem
29780  * @param {String} text The text for the tab of this TabPanelItem
29781  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29782  */
29783 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29784     /**
29785      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29786      * @type Roo.TabPanel
29787      */
29788     this.tabPanel = tabPanel;
29789     /**
29790      * The id for this TabPanelItem
29791      * @type String
29792      */
29793     this.id = id;
29794     /** @private */
29795     this.disabled = false;
29796     /** @private */
29797     this.text = text;
29798     /** @private */
29799     this.loaded = false;
29800     this.closable = closable;
29801
29802     /**
29803      * The body element for this TabPanelItem.
29804      * @type Roo.Element
29805      */
29806     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29807     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29808     this.bodyEl.setStyle("display", "block");
29809     this.bodyEl.setStyle("zoom", "1");
29810     this.hideAction();
29811
29812     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29813     /** @private */
29814     this.el = Roo.get(els.el, true);
29815     this.inner = Roo.get(els.inner, true);
29816     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29817     this.pnode = Roo.get(els.el.parentNode, true);
29818     this.el.on("mousedown", this.onTabMouseDown, this);
29819     this.el.on("click", this.onTabClick, this);
29820     /** @private */
29821     if(closable){
29822         var c = Roo.get(els.close, true);
29823         c.dom.title = this.closeText;
29824         c.addClassOnOver("close-over");
29825         c.on("click", this.closeClick, this);
29826      }
29827
29828     this.addEvents({
29829          /**
29830          * @event activate
29831          * Fires when this tab becomes the active tab.
29832          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29833          * @param {Roo.TabPanelItem} this
29834          */
29835         "activate": true,
29836         /**
29837          * @event beforeclose
29838          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29839          * @param {Roo.TabPanelItem} this
29840          * @param {Object} e Set cancel to true on this object to cancel the close.
29841          */
29842         "beforeclose": true,
29843         /**
29844          * @event close
29845          * Fires when this tab is closed.
29846          * @param {Roo.TabPanelItem} this
29847          */
29848          "close": true,
29849         /**
29850          * @event deactivate
29851          * Fires when this tab is no longer the active tab.
29852          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29853          * @param {Roo.TabPanelItem} this
29854          */
29855          "deactivate" : true
29856     });
29857     this.hidden = false;
29858
29859     Roo.TabPanelItem.superclass.constructor.call(this);
29860 };
29861
29862 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29863     purgeListeners : function(){
29864        Roo.util.Observable.prototype.purgeListeners.call(this);
29865        this.el.removeAllListeners();
29866     },
29867     /**
29868      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29869      */
29870     show : function(){
29871         this.pnode.addClass("on");
29872         this.showAction();
29873         if(Roo.isOpera){
29874             this.tabPanel.stripWrap.repaint();
29875         }
29876         this.fireEvent("activate", this.tabPanel, this);
29877     },
29878
29879     /**
29880      * Returns true if this tab is the active tab.
29881      * @return {Boolean}
29882      */
29883     isActive : function(){
29884         return this.tabPanel.getActiveTab() == this;
29885     },
29886
29887     /**
29888      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29889      */
29890     hide : function(){
29891         this.pnode.removeClass("on");
29892         this.hideAction();
29893         this.fireEvent("deactivate", this.tabPanel, this);
29894     },
29895
29896     hideAction : function(){
29897         this.bodyEl.hide();
29898         this.bodyEl.setStyle("position", "absolute");
29899         this.bodyEl.setLeft("-20000px");
29900         this.bodyEl.setTop("-20000px");
29901     },
29902
29903     showAction : function(){
29904         this.bodyEl.setStyle("position", "relative");
29905         this.bodyEl.setTop("");
29906         this.bodyEl.setLeft("");
29907         this.bodyEl.show();
29908     },
29909
29910     /**
29911      * Set the tooltip for the tab.
29912      * @param {String} tooltip The tab's tooltip
29913      */
29914     setTooltip : function(text){
29915         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29916             this.textEl.dom.qtip = text;
29917             this.textEl.dom.removeAttribute('title');
29918         }else{
29919             this.textEl.dom.title = text;
29920         }
29921     },
29922
29923     onTabClick : function(e){
29924         e.preventDefault();
29925         this.tabPanel.activate(this.id);
29926     },
29927
29928     onTabMouseDown : function(e){
29929         e.preventDefault();
29930         this.tabPanel.activate(this.id);
29931     },
29932
29933     getWidth : function(){
29934         return this.inner.getWidth();
29935     },
29936
29937     setWidth : function(width){
29938         var iwidth = width - this.pnode.getPadding("lr");
29939         this.inner.setWidth(iwidth);
29940         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29941         this.pnode.setWidth(width);
29942     },
29943
29944     /**
29945      * Show or hide the tab
29946      * @param {Boolean} hidden True to hide or false to show.
29947      */
29948     setHidden : function(hidden){
29949         this.hidden = hidden;
29950         this.pnode.setStyle("display", hidden ? "none" : "");
29951     },
29952
29953     /**
29954      * Returns true if this tab is "hidden"
29955      * @return {Boolean}
29956      */
29957     isHidden : function(){
29958         return this.hidden;
29959     },
29960
29961     /**
29962      * Returns the text for this tab
29963      * @return {String}
29964      */
29965     getText : function(){
29966         return this.text;
29967     },
29968
29969     autoSize : function(){
29970         //this.el.beginMeasure();
29971         this.textEl.setWidth(1);
29972         /*
29973          *  #2804 [new] Tabs in Roojs
29974          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29975          */
29976         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29977         //this.el.endMeasure();
29978     },
29979
29980     /**
29981      * Sets the text for the tab (Note: this also sets the tooltip text)
29982      * @param {String} text The tab's text and tooltip
29983      */
29984     setText : function(text){
29985         this.text = text;
29986         this.textEl.update(text);
29987         this.setTooltip(text);
29988         if(!this.tabPanel.resizeTabs){
29989             this.autoSize();
29990         }
29991     },
29992     /**
29993      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29994      */
29995     activate : function(){
29996         this.tabPanel.activate(this.id);
29997     },
29998
29999     /**
30000      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30001      */
30002     disable : function(){
30003         if(this.tabPanel.active != this){
30004             this.disabled = true;
30005             this.pnode.addClass("disabled");
30006         }
30007     },
30008
30009     /**
30010      * Enables this TabPanelItem if it was previously disabled.
30011      */
30012     enable : function(){
30013         this.disabled = false;
30014         this.pnode.removeClass("disabled");
30015     },
30016
30017     /**
30018      * Sets the content for this TabPanelItem.
30019      * @param {String} content The content
30020      * @param {Boolean} loadScripts true to look for and load scripts
30021      */
30022     setContent : function(content, loadScripts){
30023         this.bodyEl.update(content, loadScripts);
30024     },
30025
30026     /**
30027      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30028      * @return {Roo.UpdateManager} The UpdateManager
30029      */
30030     getUpdateManager : function(){
30031         return this.bodyEl.getUpdateManager();
30032     },
30033
30034     /**
30035      * Set a URL to be used to load the content for this TabPanelItem.
30036      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30037      * @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)
30038      * @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)
30039      * @return {Roo.UpdateManager} The UpdateManager
30040      */
30041     setUrl : function(url, params, loadOnce){
30042         if(this.refreshDelegate){
30043             this.un('activate', this.refreshDelegate);
30044         }
30045         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30046         this.on("activate", this.refreshDelegate);
30047         return this.bodyEl.getUpdateManager();
30048     },
30049
30050     /** @private */
30051     _handleRefresh : function(url, params, loadOnce){
30052         if(!loadOnce || !this.loaded){
30053             var updater = this.bodyEl.getUpdateManager();
30054             updater.update(url, params, this._setLoaded.createDelegate(this));
30055         }
30056     },
30057
30058     /**
30059      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30060      *   Will fail silently if the setUrl method has not been called.
30061      *   This does not activate the panel, just updates its content.
30062      */
30063     refresh : function(){
30064         if(this.refreshDelegate){
30065            this.loaded = false;
30066            this.refreshDelegate();
30067         }
30068     },
30069
30070     /** @private */
30071     _setLoaded : function(){
30072         this.loaded = true;
30073     },
30074
30075     /** @private */
30076     closeClick : function(e){
30077         var o = {};
30078         e.stopEvent();
30079         this.fireEvent("beforeclose", this, o);
30080         if(o.cancel !== true){
30081             this.tabPanel.removeTab(this.id);
30082         }
30083     },
30084     /**
30085      * The text displayed in the tooltip for the close icon.
30086      * @type String
30087      */
30088     closeText : "Close this tab"
30089 });
30090
30091 /** @private */
30092 Roo.TabPanel.prototype.createStrip = function(container){
30093     var strip = document.createElement("div");
30094     strip.className = "x-tabs-wrap";
30095     container.appendChild(strip);
30096     return strip;
30097 };
30098 /** @private */
30099 Roo.TabPanel.prototype.createStripList = function(strip){
30100     // div wrapper for retard IE
30101     // returns the "tr" element.
30102     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30103         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30104         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30105     return strip.firstChild.firstChild.firstChild.firstChild;
30106 };
30107 /** @private */
30108 Roo.TabPanel.prototype.createBody = function(container){
30109     var body = document.createElement("div");
30110     Roo.id(body, "tab-body");
30111     Roo.fly(body).addClass("x-tabs-body");
30112     container.appendChild(body);
30113     return body;
30114 };
30115 /** @private */
30116 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30117     var body = Roo.getDom(id);
30118     if(!body){
30119         body = document.createElement("div");
30120         body.id = id;
30121     }
30122     Roo.fly(body).addClass("x-tabs-item-body");
30123     bodyEl.insertBefore(body, bodyEl.firstChild);
30124     return body;
30125 };
30126 /** @private */
30127 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30128     var td = document.createElement("td");
30129     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30130     //stripEl.appendChild(td);
30131     if(closable){
30132         td.className = "x-tabs-closable";
30133         if(!this.closeTpl){
30134             this.closeTpl = new Roo.Template(
30135                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30136                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30137                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30138             );
30139         }
30140         var el = this.closeTpl.overwrite(td, {"text": text});
30141         var close = el.getElementsByTagName("div")[0];
30142         var inner = el.getElementsByTagName("em")[0];
30143         return {"el": el, "close": close, "inner": inner};
30144     } else {
30145         if(!this.tabTpl){
30146             this.tabTpl = new Roo.Template(
30147                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30148                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30149             );
30150         }
30151         var el = this.tabTpl.overwrite(td, {"text": text});
30152         var inner = el.getElementsByTagName("em")[0];
30153         return {"el": el, "inner": inner};
30154     }
30155 };/*
30156  * Based on:
30157  * Ext JS Library 1.1.1
30158  * Copyright(c) 2006-2007, Ext JS, LLC.
30159  *
30160  * Originally Released Under LGPL - original licence link has changed is not relivant.
30161  *
30162  * Fork - LGPL
30163  * <script type="text/javascript">
30164  */
30165
30166 /**
30167  * @class Roo.Button
30168  * @extends Roo.util.Observable
30169  * Simple Button class
30170  * @cfg {String} text The button text
30171  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30172  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30173  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30174  * @cfg {Object} scope The scope of the handler
30175  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30176  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30177  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30178  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30179  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30180  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30181    applies if enableToggle = true)
30182  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30183  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30184   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30185  * @constructor
30186  * Create a new button
30187  * @param {Object} config The config object
30188  */
30189 Roo.Button = function(renderTo, config)
30190 {
30191     if (!config) {
30192         config = renderTo;
30193         renderTo = config.renderTo || false;
30194     }
30195     
30196     Roo.apply(this, config);
30197     this.addEvents({
30198         /**
30199              * @event click
30200              * Fires when this button is clicked
30201              * @param {Button} this
30202              * @param {EventObject} e The click event
30203              */
30204             "click" : true,
30205         /**
30206              * @event toggle
30207              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30208              * @param {Button} this
30209              * @param {Boolean} pressed
30210              */
30211             "toggle" : true,
30212         /**
30213              * @event mouseover
30214              * Fires when the mouse hovers over the button
30215              * @param {Button} this
30216              * @param {Event} e The event object
30217              */
30218         'mouseover' : true,
30219         /**
30220              * @event mouseout
30221              * Fires when the mouse exits the button
30222              * @param {Button} this
30223              * @param {Event} e The event object
30224              */
30225         'mouseout': true,
30226          /**
30227              * @event render
30228              * Fires when the button is rendered
30229              * @param {Button} this
30230              */
30231         'render': true
30232     });
30233     if(this.menu){
30234         this.menu = Roo.menu.MenuMgr.get(this.menu);
30235     }
30236     // register listeners first!!  - so render can be captured..
30237     Roo.util.Observable.call(this);
30238     if(renderTo){
30239         this.render(renderTo);
30240     }
30241     
30242   
30243 };
30244
30245 Roo.extend(Roo.Button, Roo.util.Observable, {
30246     /**
30247      * 
30248      */
30249     
30250     /**
30251      * Read-only. True if this button is hidden
30252      * @type Boolean
30253      */
30254     hidden : false,
30255     /**
30256      * Read-only. True if this button is disabled
30257      * @type Boolean
30258      */
30259     disabled : false,
30260     /**
30261      * Read-only. True if this button is pressed (only if enableToggle = true)
30262      * @type Boolean
30263      */
30264     pressed : false,
30265
30266     /**
30267      * @cfg {Number} tabIndex 
30268      * The DOM tabIndex for this button (defaults to undefined)
30269      */
30270     tabIndex : undefined,
30271
30272     /**
30273      * @cfg {Boolean} enableToggle
30274      * True to enable pressed/not pressed toggling (defaults to false)
30275      */
30276     enableToggle: false,
30277     /**
30278      * @cfg {Roo.menu.Menu} menu
30279      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30280      */
30281     menu : undefined,
30282     /**
30283      * @cfg {String} menuAlign
30284      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30285      */
30286     menuAlign : "tl-bl?",
30287
30288     /**
30289      * @cfg {String} iconCls
30290      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30291      */
30292     iconCls : undefined,
30293     /**
30294      * @cfg {String} type
30295      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30296      */
30297     type : 'button',
30298
30299     // private
30300     menuClassTarget: 'tr',
30301
30302     /**
30303      * @cfg {String} clickEvent
30304      * The type of event to map to the button's event handler (defaults to 'click')
30305      */
30306     clickEvent : 'click',
30307
30308     /**
30309      * @cfg {Boolean} handleMouseEvents
30310      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30311      */
30312     handleMouseEvents : true,
30313
30314     /**
30315      * @cfg {String} tooltipType
30316      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30317      */
30318     tooltipType : 'qtip',
30319
30320     /**
30321      * @cfg {String} cls
30322      * A CSS class to apply to the button's main element.
30323      */
30324     
30325     /**
30326      * @cfg {Roo.Template} template (Optional)
30327      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30328      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30329      * require code modifications if required elements (e.g. a button) aren't present.
30330      */
30331
30332     // private
30333     render : function(renderTo){
30334         var btn;
30335         if(this.hideParent){
30336             this.parentEl = Roo.get(renderTo);
30337         }
30338         if(!this.dhconfig){
30339             if(!this.template){
30340                 if(!Roo.Button.buttonTemplate){
30341                     // hideous table template
30342                     Roo.Button.buttonTemplate = new Roo.Template(
30343                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30344                         '<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>',
30345                         "</tr></tbody></table>");
30346                 }
30347                 this.template = Roo.Button.buttonTemplate;
30348             }
30349             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30350             var btnEl = btn.child("button:first");
30351             btnEl.on('focus', this.onFocus, this);
30352             btnEl.on('blur', this.onBlur, this);
30353             if(this.cls){
30354                 btn.addClass(this.cls);
30355             }
30356             if(this.icon){
30357                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30358             }
30359             if(this.iconCls){
30360                 btnEl.addClass(this.iconCls);
30361                 if(!this.cls){
30362                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30363                 }
30364             }
30365             if(this.tabIndex !== undefined){
30366                 btnEl.dom.tabIndex = this.tabIndex;
30367             }
30368             if(this.tooltip){
30369                 if(typeof this.tooltip == 'object'){
30370                     Roo.QuickTips.tips(Roo.apply({
30371                           target: btnEl.id
30372                     }, this.tooltip));
30373                 } else {
30374                     btnEl.dom[this.tooltipType] = this.tooltip;
30375                 }
30376             }
30377         }else{
30378             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30379         }
30380         this.el = btn;
30381         if(this.id){
30382             this.el.dom.id = this.el.id = this.id;
30383         }
30384         if(this.menu){
30385             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30386             this.menu.on("show", this.onMenuShow, this);
30387             this.menu.on("hide", this.onMenuHide, this);
30388         }
30389         btn.addClass("x-btn");
30390         if(Roo.isIE && !Roo.isIE7){
30391             this.autoWidth.defer(1, this);
30392         }else{
30393             this.autoWidth();
30394         }
30395         if(this.handleMouseEvents){
30396             btn.on("mouseover", this.onMouseOver, this);
30397             btn.on("mouseout", this.onMouseOut, this);
30398             btn.on("mousedown", this.onMouseDown, this);
30399         }
30400         btn.on(this.clickEvent, this.onClick, this);
30401         //btn.on("mouseup", this.onMouseUp, this);
30402         if(this.hidden){
30403             this.hide();
30404         }
30405         if(this.disabled){
30406             this.disable();
30407         }
30408         Roo.ButtonToggleMgr.register(this);
30409         if(this.pressed){
30410             this.el.addClass("x-btn-pressed");
30411         }
30412         if(this.repeat){
30413             var repeater = new Roo.util.ClickRepeater(btn,
30414                 typeof this.repeat == "object" ? this.repeat : {}
30415             );
30416             repeater.on("click", this.onClick,  this);
30417         }
30418         
30419         this.fireEvent('render', this);
30420         
30421     },
30422     /**
30423      * Returns the button's underlying element
30424      * @return {Roo.Element} The element
30425      */
30426     getEl : function(){
30427         return this.el;  
30428     },
30429     
30430     /**
30431      * Destroys this Button and removes any listeners.
30432      */
30433     destroy : function(){
30434         Roo.ButtonToggleMgr.unregister(this);
30435         this.el.removeAllListeners();
30436         this.purgeListeners();
30437         this.el.remove();
30438     },
30439
30440     // private
30441     autoWidth : function(){
30442         if(this.el){
30443             this.el.setWidth("auto");
30444             if(Roo.isIE7 && Roo.isStrict){
30445                 var ib = this.el.child('button');
30446                 if(ib && ib.getWidth() > 20){
30447                     ib.clip();
30448                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30449                 }
30450             }
30451             if(this.minWidth){
30452                 if(this.hidden){
30453                     this.el.beginMeasure();
30454                 }
30455                 if(this.el.getWidth() < this.minWidth){
30456                     this.el.setWidth(this.minWidth);
30457                 }
30458                 if(this.hidden){
30459                     this.el.endMeasure();
30460                 }
30461             }
30462         }
30463     },
30464
30465     /**
30466      * Assigns this button's click handler
30467      * @param {Function} handler The function to call when the button is clicked
30468      * @param {Object} scope (optional) Scope for the function passed in
30469      */
30470     setHandler : function(handler, scope){
30471         this.handler = handler;
30472         this.scope = scope;  
30473     },
30474     
30475     /**
30476      * Sets this button's text
30477      * @param {String} text The button text
30478      */
30479     setText : function(text){
30480         this.text = text;
30481         if(this.el){
30482             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30483         }
30484         this.autoWidth();
30485     },
30486     
30487     /**
30488      * Gets the text for this button
30489      * @return {String} The button text
30490      */
30491     getText : function(){
30492         return this.text;  
30493     },
30494     
30495     /**
30496      * Show this button
30497      */
30498     show: function(){
30499         this.hidden = false;
30500         if(this.el){
30501             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30502         }
30503     },
30504     
30505     /**
30506      * Hide this button
30507      */
30508     hide: function(){
30509         this.hidden = true;
30510         if(this.el){
30511             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30512         }
30513     },
30514     
30515     /**
30516      * Convenience function for boolean show/hide
30517      * @param {Boolean} visible True to show, false to hide
30518      */
30519     setVisible: function(visible){
30520         if(visible) {
30521             this.show();
30522         }else{
30523             this.hide();
30524         }
30525     },
30526     
30527     /**
30528      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30529      * @param {Boolean} state (optional) Force a particular state
30530      */
30531     toggle : function(state){
30532         state = state === undefined ? !this.pressed : state;
30533         if(state != this.pressed){
30534             if(state){
30535                 this.el.addClass("x-btn-pressed");
30536                 this.pressed = true;
30537                 this.fireEvent("toggle", this, true);
30538             }else{
30539                 this.el.removeClass("x-btn-pressed");
30540                 this.pressed = false;
30541                 this.fireEvent("toggle", this, false);
30542             }
30543             if(this.toggleHandler){
30544                 this.toggleHandler.call(this.scope || this, this, state);
30545             }
30546         }
30547     },
30548     
30549     /**
30550      * Focus the button
30551      */
30552     focus : function(){
30553         this.el.child('button:first').focus();
30554     },
30555     
30556     /**
30557      * Disable this button
30558      */
30559     disable : function(){
30560         if(this.el){
30561             this.el.addClass("x-btn-disabled");
30562         }
30563         this.disabled = true;
30564     },
30565     
30566     /**
30567      * Enable this button
30568      */
30569     enable : function(){
30570         if(this.el){
30571             this.el.removeClass("x-btn-disabled");
30572         }
30573         this.disabled = false;
30574     },
30575
30576     /**
30577      * Convenience function for boolean enable/disable
30578      * @param {Boolean} enabled True to enable, false to disable
30579      */
30580     setDisabled : function(v){
30581         this[v !== true ? "enable" : "disable"]();
30582     },
30583
30584     // private
30585     onClick : function(e)
30586     {
30587         if(e){
30588             e.preventDefault();
30589         }
30590         if(e.button != 0){
30591             return;
30592         }
30593         if(!this.disabled){
30594             if(this.enableToggle){
30595                 this.toggle();
30596             }
30597             if(this.menu && !this.menu.isVisible()){
30598                 this.menu.show(this.el, this.menuAlign);
30599             }
30600             this.fireEvent("click", this, e);
30601             if(this.handler){
30602                 this.el.removeClass("x-btn-over");
30603                 this.handler.call(this.scope || this, this, e);
30604             }
30605         }
30606     },
30607     // private
30608     onMouseOver : function(e){
30609         if(!this.disabled){
30610             this.el.addClass("x-btn-over");
30611             this.fireEvent('mouseover', this, e);
30612         }
30613     },
30614     // private
30615     onMouseOut : function(e){
30616         if(!e.within(this.el,  true)){
30617             this.el.removeClass("x-btn-over");
30618             this.fireEvent('mouseout', this, e);
30619         }
30620     },
30621     // private
30622     onFocus : function(e){
30623         if(!this.disabled){
30624             this.el.addClass("x-btn-focus");
30625         }
30626     },
30627     // private
30628     onBlur : function(e){
30629         this.el.removeClass("x-btn-focus");
30630     },
30631     // private
30632     onMouseDown : function(e){
30633         if(!this.disabled && e.button == 0){
30634             this.el.addClass("x-btn-click");
30635             Roo.get(document).on('mouseup', this.onMouseUp, this);
30636         }
30637     },
30638     // private
30639     onMouseUp : function(e){
30640         if(e.button == 0){
30641             this.el.removeClass("x-btn-click");
30642             Roo.get(document).un('mouseup', this.onMouseUp, this);
30643         }
30644     },
30645     // private
30646     onMenuShow : function(e){
30647         this.el.addClass("x-btn-menu-active");
30648     },
30649     // private
30650     onMenuHide : function(e){
30651         this.el.removeClass("x-btn-menu-active");
30652     }   
30653 });
30654
30655 // Private utility class used by Button
30656 Roo.ButtonToggleMgr = function(){
30657    var groups = {};
30658    
30659    function toggleGroup(btn, state){
30660        if(state){
30661            var g = groups[btn.toggleGroup];
30662            for(var i = 0, l = g.length; i < l; i++){
30663                if(g[i] != btn){
30664                    g[i].toggle(false);
30665                }
30666            }
30667        }
30668    }
30669    
30670    return {
30671        register : function(btn){
30672            if(!btn.toggleGroup){
30673                return;
30674            }
30675            var g = groups[btn.toggleGroup];
30676            if(!g){
30677                g = groups[btn.toggleGroup] = [];
30678            }
30679            g.push(btn);
30680            btn.on("toggle", toggleGroup);
30681        },
30682        
30683        unregister : function(btn){
30684            if(!btn.toggleGroup){
30685                return;
30686            }
30687            var g = groups[btn.toggleGroup];
30688            if(g){
30689                g.remove(btn);
30690                btn.un("toggle", toggleGroup);
30691            }
30692        }
30693    };
30694 }();/*
30695  * Based on:
30696  * Ext JS Library 1.1.1
30697  * Copyright(c) 2006-2007, Ext JS, LLC.
30698  *
30699  * Originally Released Under LGPL - original licence link has changed is not relivant.
30700  *
30701  * Fork - LGPL
30702  * <script type="text/javascript">
30703  */
30704  
30705 /**
30706  * @class Roo.SplitButton
30707  * @extends Roo.Button
30708  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30709  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30710  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30711  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30712  * @cfg {String} arrowTooltip The title attribute of the arrow
30713  * @constructor
30714  * Create a new menu button
30715  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30716  * @param {Object} config The config object
30717  */
30718 Roo.SplitButton = function(renderTo, config){
30719     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30720     /**
30721      * @event arrowclick
30722      * Fires when this button's arrow is clicked
30723      * @param {SplitButton} this
30724      * @param {EventObject} e The click event
30725      */
30726     this.addEvents({"arrowclick":true});
30727 };
30728
30729 Roo.extend(Roo.SplitButton, Roo.Button, {
30730     render : function(renderTo){
30731         // this is one sweet looking template!
30732         var tpl = new Roo.Template(
30733             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30734             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30735             '<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>',
30736             "</tbody></table></td><td>",
30737             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30738             '<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>',
30739             "</tbody></table></td></tr></table>"
30740         );
30741         var btn = tpl.append(renderTo, [this.text, this.type], true);
30742         var btnEl = btn.child("button");
30743         if(this.cls){
30744             btn.addClass(this.cls);
30745         }
30746         if(this.icon){
30747             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30748         }
30749         if(this.iconCls){
30750             btnEl.addClass(this.iconCls);
30751             if(!this.cls){
30752                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30753             }
30754         }
30755         this.el = btn;
30756         if(this.handleMouseEvents){
30757             btn.on("mouseover", this.onMouseOver, this);
30758             btn.on("mouseout", this.onMouseOut, this);
30759             btn.on("mousedown", this.onMouseDown, this);
30760             btn.on("mouseup", this.onMouseUp, this);
30761         }
30762         btn.on(this.clickEvent, this.onClick, this);
30763         if(this.tooltip){
30764             if(typeof this.tooltip == 'object'){
30765                 Roo.QuickTips.tips(Roo.apply({
30766                       target: btnEl.id
30767                 }, this.tooltip));
30768             } else {
30769                 btnEl.dom[this.tooltipType] = this.tooltip;
30770             }
30771         }
30772         if(this.arrowTooltip){
30773             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30774         }
30775         if(this.hidden){
30776             this.hide();
30777         }
30778         if(this.disabled){
30779             this.disable();
30780         }
30781         if(this.pressed){
30782             this.el.addClass("x-btn-pressed");
30783         }
30784         if(Roo.isIE && !Roo.isIE7){
30785             this.autoWidth.defer(1, this);
30786         }else{
30787             this.autoWidth();
30788         }
30789         if(this.menu){
30790             this.menu.on("show", this.onMenuShow, this);
30791             this.menu.on("hide", this.onMenuHide, this);
30792         }
30793         this.fireEvent('render', this);
30794     },
30795
30796     // private
30797     autoWidth : function(){
30798         if(this.el){
30799             var tbl = this.el.child("table:first");
30800             var tbl2 = this.el.child("table:last");
30801             this.el.setWidth("auto");
30802             tbl.setWidth("auto");
30803             if(Roo.isIE7 && Roo.isStrict){
30804                 var ib = this.el.child('button:first');
30805                 if(ib && ib.getWidth() > 20){
30806                     ib.clip();
30807                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30808                 }
30809             }
30810             if(this.minWidth){
30811                 if(this.hidden){
30812                     this.el.beginMeasure();
30813                 }
30814                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30815                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30816                 }
30817                 if(this.hidden){
30818                     this.el.endMeasure();
30819                 }
30820             }
30821             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30822         } 
30823     },
30824     /**
30825      * Sets this button's click handler
30826      * @param {Function} handler The function to call when the button is clicked
30827      * @param {Object} scope (optional) Scope for the function passed above
30828      */
30829     setHandler : function(handler, scope){
30830         this.handler = handler;
30831         this.scope = scope;  
30832     },
30833     
30834     /**
30835      * Sets this button's arrow click handler
30836      * @param {Function} handler The function to call when the arrow is clicked
30837      * @param {Object} scope (optional) Scope for the function passed above
30838      */
30839     setArrowHandler : function(handler, scope){
30840         this.arrowHandler = handler;
30841         this.scope = scope;  
30842     },
30843     
30844     /**
30845      * Focus the button
30846      */
30847     focus : function(){
30848         if(this.el){
30849             this.el.child("button:first").focus();
30850         }
30851     },
30852
30853     // private
30854     onClick : function(e){
30855         e.preventDefault();
30856         if(!this.disabled){
30857             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30858                 if(this.menu && !this.menu.isVisible()){
30859                     this.menu.show(this.el, this.menuAlign);
30860                 }
30861                 this.fireEvent("arrowclick", this, e);
30862                 if(this.arrowHandler){
30863                     this.arrowHandler.call(this.scope || this, this, e);
30864                 }
30865             }else{
30866                 this.fireEvent("click", this, e);
30867                 if(this.handler){
30868                     this.handler.call(this.scope || this, this, e);
30869                 }
30870             }
30871         }
30872     },
30873     // private
30874     onMouseDown : function(e){
30875         if(!this.disabled){
30876             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30877         }
30878     },
30879     // private
30880     onMouseUp : function(e){
30881         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30882     }   
30883 });
30884
30885
30886 // backwards compat
30887 Roo.MenuButton = Roo.SplitButton;/*
30888  * Based on:
30889  * Ext JS Library 1.1.1
30890  * Copyright(c) 2006-2007, Ext JS, LLC.
30891  *
30892  * Originally Released Under LGPL - original licence link has changed is not relivant.
30893  *
30894  * Fork - LGPL
30895  * <script type="text/javascript">
30896  */
30897
30898 /**
30899  * @class Roo.Toolbar
30900  * @children   Roo.Toolbar.Item Roo.form.Field
30901  * Basic Toolbar class.
30902  * @constructor
30903  * Creates a new Toolbar
30904  * @param {Object} container The config object
30905  */ 
30906 Roo.Toolbar = function(container, buttons, config)
30907 {
30908     /// old consturctor format still supported..
30909     if(container instanceof Array){ // omit the container for later rendering
30910         buttons = container;
30911         config = buttons;
30912         container = null;
30913     }
30914     if (typeof(container) == 'object' && container.xtype) {
30915         config = container;
30916         container = config.container;
30917         buttons = config.buttons || []; // not really - use items!!
30918     }
30919     var xitems = [];
30920     if (config && config.items) {
30921         xitems = config.items;
30922         delete config.items;
30923     }
30924     Roo.apply(this, config);
30925     this.buttons = buttons;
30926     
30927     if(container){
30928         this.render(container);
30929     }
30930     this.xitems = xitems;
30931     Roo.each(xitems, function(b) {
30932         this.add(b);
30933     }, this);
30934     
30935 };
30936
30937 Roo.Toolbar.prototype = {
30938     /**
30939      * @cfg {Array} items
30940      * array of button configs or elements to add (will be converted to a MixedCollection)
30941      */
30942     items: false,
30943     /**
30944      * @cfg {String/HTMLElement/Element} container
30945      * The id or element that will contain the toolbar
30946      */
30947     // private
30948     render : function(ct){
30949         this.el = Roo.get(ct);
30950         if(this.cls){
30951             this.el.addClass(this.cls);
30952         }
30953         // using a table allows for vertical alignment
30954         // 100% width is needed by Safari...
30955         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30956         this.tr = this.el.child("tr", true);
30957         var autoId = 0;
30958         this.items = new Roo.util.MixedCollection(false, function(o){
30959             return o.id || ("item" + (++autoId));
30960         });
30961         if(this.buttons){
30962             this.add.apply(this, this.buttons);
30963             delete this.buttons;
30964         }
30965     },
30966
30967     /**
30968      * Adds element(s) to the toolbar -- this function takes a variable number of 
30969      * arguments of mixed type and adds them to the toolbar.
30970      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30971      * <ul>
30972      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30973      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30974      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30975      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30976      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30977      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30978      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30979      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30980      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30981      * </ul>
30982      * @param {Mixed} arg2
30983      * @param {Mixed} etc.
30984      */
30985     add : function(){
30986         var a = arguments, l = a.length;
30987         for(var i = 0; i < l; i++){
30988             this._add(a[i]);
30989         }
30990     },
30991     // private..
30992     _add : function(el) {
30993         
30994         if (el.xtype) {
30995             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30996         }
30997         
30998         if (el.applyTo){ // some kind of form field
30999             return this.addField(el);
31000         } 
31001         if (el.render){ // some kind of Toolbar.Item
31002             return this.addItem(el);
31003         }
31004         if (typeof el == "string"){ // string
31005             if(el == "separator" || el == "-"){
31006                 return this.addSeparator();
31007             }
31008             if (el == " "){
31009                 return this.addSpacer();
31010             }
31011             if(el == "->"){
31012                 return this.addFill();
31013             }
31014             return this.addText(el);
31015             
31016         }
31017         if(el.tagName){ // element
31018             return this.addElement(el);
31019         }
31020         if(typeof el == "object"){ // must be button config?
31021             return this.addButton(el);
31022         }
31023         // and now what?!?!
31024         return false;
31025         
31026     },
31027     
31028     /**
31029      * Add an Xtype element
31030      * @param {Object} xtype Xtype Object
31031      * @return {Object} created Object
31032      */
31033     addxtype : function(e){
31034         return this.add(e);  
31035     },
31036     
31037     /**
31038      * Returns the Element for this toolbar.
31039      * @return {Roo.Element}
31040      */
31041     getEl : function(){
31042         return this.el;  
31043     },
31044     
31045     /**
31046      * Adds a separator
31047      * @return {Roo.Toolbar.Item} The separator item
31048      */
31049     addSeparator : function(){
31050         return this.addItem(new Roo.Toolbar.Separator());
31051     },
31052
31053     /**
31054      * Adds a spacer element
31055      * @return {Roo.Toolbar.Spacer} The spacer item
31056      */
31057     addSpacer : function(){
31058         return this.addItem(new Roo.Toolbar.Spacer());
31059     },
31060
31061     /**
31062      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31063      * @return {Roo.Toolbar.Fill} The fill item
31064      */
31065     addFill : function(){
31066         return this.addItem(new Roo.Toolbar.Fill());
31067     },
31068
31069     /**
31070      * Adds any standard HTML element to the toolbar
31071      * @param {String/HTMLElement/Element} el The element or id of the element to add
31072      * @return {Roo.Toolbar.Item} The element's item
31073      */
31074     addElement : function(el){
31075         return this.addItem(new Roo.Toolbar.Item(el));
31076     },
31077     /**
31078      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31079      * @type Roo.util.MixedCollection  
31080      */
31081     items : false,
31082      
31083     /**
31084      * Adds any Toolbar.Item or subclass
31085      * @param {Roo.Toolbar.Item} item
31086      * @return {Roo.Toolbar.Item} The item
31087      */
31088     addItem : function(item){
31089         var td = this.nextBlock();
31090         item.render(td);
31091         this.items.add(item);
31092         return item;
31093     },
31094     
31095     /**
31096      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31097      * @param {Object/Array} config A button config or array of configs
31098      * @return {Roo.Toolbar.Button/Array}
31099      */
31100     addButton : function(config){
31101         if(config instanceof Array){
31102             var buttons = [];
31103             for(var i = 0, len = config.length; i < len; i++) {
31104                 buttons.push(this.addButton(config[i]));
31105             }
31106             return buttons;
31107         }
31108         var b = config;
31109         if(!(config instanceof Roo.Toolbar.Button)){
31110             b = config.split ?
31111                 new Roo.Toolbar.SplitButton(config) :
31112                 new Roo.Toolbar.Button(config);
31113         }
31114         var td = this.nextBlock();
31115         b.render(td);
31116         this.items.add(b);
31117         return b;
31118     },
31119     
31120     /**
31121      * Adds text to the toolbar
31122      * @param {String} text The text to add
31123      * @return {Roo.Toolbar.Item} The element's item
31124      */
31125     addText : function(text){
31126         return this.addItem(new Roo.Toolbar.TextItem(text));
31127     },
31128     
31129     /**
31130      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31131      * @param {Number} index The index where the item is to be inserted
31132      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31133      * @return {Roo.Toolbar.Button/Item}
31134      */
31135     insertButton : function(index, item){
31136         if(item instanceof Array){
31137             var buttons = [];
31138             for(var i = 0, len = item.length; i < len; i++) {
31139                buttons.push(this.insertButton(index + i, item[i]));
31140             }
31141             return buttons;
31142         }
31143         if (!(item instanceof Roo.Toolbar.Button)){
31144            item = new Roo.Toolbar.Button(item);
31145         }
31146         var td = document.createElement("td");
31147         this.tr.insertBefore(td, this.tr.childNodes[index]);
31148         item.render(td);
31149         this.items.insert(index, item);
31150         return item;
31151     },
31152     
31153     /**
31154      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31155      * @param {Object} config
31156      * @return {Roo.Toolbar.Item} The element's item
31157      */
31158     addDom : function(config, returnEl){
31159         var td = this.nextBlock();
31160         Roo.DomHelper.overwrite(td, config);
31161         var ti = new Roo.Toolbar.Item(td.firstChild);
31162         ti.render(td);
31163         this.items.add(ti);
31164         return ti;
31165     },
31166
31167     /**
31168      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31169      * @type Roo.util.MixedCollection  
31170      */
31171     fields : false,
31172     
31173     /**
31174      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31175      * Note: the field should not have been rendered yet. For a field that has already been
31176      * rendered, use {@link #addElement}.
31177      * @param {Roo.form.Field} field
31178      * @return {Roo.ToolbarItem}
31179      */
31180      
31181       
31182     addField : function(field) {
31183         if (!this.fields) {
31184             var autoId = 0;
31185             this.fields = new Roo.util.MixedCollection(false, function(o){
31186                 return o.id || ("item" + (++autoId));
31187             });
31188
31189         }
31190         
31191         var td = this.nextBlock();
31192         field.render(td);
31193         var ti = new Roo.Toolbar.Item(td.firstChild);
31194         ti.render(td);
31195         this.items.add(ti);
31196         this.fields.add(field);
31197         return ti;
31198     },
31199     /**
31200      * Hide the toolbar
31201      * @method hide
31202      */
31203      
31204       
31205     hide : function()
31206     {
31207         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31208         this.el.child('div').hide();
31209     },
31210     /**
31211      * Show the toolbar
31212      * @method show
31213      */
31214     show : function()
31215     {
31216         this.el.child('div').show();
31217     },
31218       
31219     // private
31220     nextBlock : function(){
31221         var td = document.createElement("td");
31222         this.tr.appendChild(td);
31223         return td;
31224     },
31225
31226     // private
31227     destroy : function(){
31228         if(this.items){ // rendered?
31229             Roo.destroy.apply(Roo, this.items.items);
31230         }
31231         if(this.fields){ // rendered?
31232             Roo.destroy.apply(Roo, this.fields.items);
31233         }
31234         Roo.Element.uncache(this.el, this.tr);
31235     }
31236 };
31237
31238 /**
31239  * @class Roo.Toolbar.Item
31240  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31241  * @constructor
31242  * Creates a new Item
31243  * @param {HTMLElement} el 
31244  */
31245 Roo.Toolbar.Item = function(el){
31246     var cfg = {};
31247     if (typeof (el.xtype) != 'undefined') {
31248         cfg = el;
31249         el = cfg.el;
31250     }
31251     
31252     this.el = Roo.getDom(el);
31253     this.id = Roo.id(this.el);
31254     this.hidden = false;
31255     
31256     this.addEvents({
31257          /**
31258              * @event render
31259              * Fires when the button is rendered
31260              * @param {Button} this
31261              */
31262         'render': true
31263     });
31264     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31265 };
31266 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31267 //Roo.Toolbar.Item.prototype = {
31268     
31269     /**
31270      * Get this item's HTML Element
31271      * @return {HTMLElement}
31272      */
31273     getEl : function(){
31274        return this.el;  
31275     },
31276
31277     // private
31278     render : function(td){
31279         
31280          this.td = td;
31281         td.appendChild(this.el);
31282         
31283         this.fireEvent('render', this);
31284     },
31285     
31286     /**
31287      * Removes and destroys this item.
31288      */
31289     destroy : function(){
31290         this.td.parentNode.removeChild(this.td);
31291     },
31292     
31293     /**
31294      * Shows this item.
31295      */
31296     show: function(){
31297         this.hidden = false;
31298         this.td.style.display = "";
31299     },
31300     
31301     /**
31302      * Hides this item.
31303      */
31304     hide: function(){
31305         this.hidden = true;
31306         this.td.style.display = "none";
31307     },
31308     
31309     /**
31310      * Convenience function for boolean show/hide.
31311      * @param {Boolean} visible true to show/false to hide
31312      */
31313     setVisible: function(visible){
31314         if(visible) {
31315             this.show();
31316         }else{
31317             this.hide();
31318         }
31319     },
31320     
31321     /**
31322      * Try to focus this item.
31323      */
31324     focus : function(){
31325         Roo.fly(this.el).focus();
31326     },
31327     
31328     /**
31329      * Disables this item.
31330      */
31331     disable : function(){
31332         Roo.fly(this.td).addClass("x-item-disabled");
31333         this.disabled = true;
31334         this.el.disabled = true;
31335     },
31336     
31337     /**
31338      * Enables this item.
31339      */
31340     enable : function(){
31341         Roo.fly(this.td).removeClass("x-item-disabled");
31342         this.disabled = false;
31343         this.el.disabled = false;
31344     }
31345 });
31346
31347
31348 /**
31349  * @class Roo.Toolbar.Separator
31350  * @extends Roo.Toolbar.Item
31351  * A simple toolbar separator class
31352  * @constructor
31353  * Creates a new Separator
31354  */
31355 Roo.Toolbar.Separator = function(cfg){
31356     
31357     var s = document.createElement("span");
31358     s.className = "ytb-sep";
31359     if (cfg) {
31360         cfg.el = s;
31361     }
31362     
31363     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31364 };
31365 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31366     enable:Roo.emptyFn,
31367     disable:Roo.emptyFn,
31368     focus:Roo.emptyFn
31369 });
31370
31371 /**
31372  * @class Roo.Toolbar.Spacer
31373  * @extends Roo.Toolbar.Item
31374  * A simple element that adds extra horizontal space to a toolbar.
31375  * @constructor
31376  * Creates a new Spacer
31377  */
31378 Roo.Toolbar.Spacer = function(cfg){
31379     var s = document.createElement("div");
31380     s.className = "ytb-spacer";
31381     if (cfg) {
31382         cfg.el = s;
31383     }
31384     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31385 };
31386 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31387     enable:Roo.emptyFn,
31388     disable:Roo.emptyFn,
31389     focus:Roo.emptyFn
31390 });
31391
31392 /**
31393  * @class Roo.Toolbar.Fill
31394  * @extends Roo.Toolbar.Spacer
31395  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31396  * @constructor
31397  * Creates a new Spacer
31398  */
31399 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31400     // private
31401     render : function(td){
31402         td.style.width = '100%';
31403         Roo.Toolbar.Fill.superclass.render.call(this, td);
31404     }
31405 });
31406
31407 /**
31408  * @class Roo.Toolbar.TextItem
31409  * @extends Roo.Toolbar.Item
31410  * A simple class that renders text directly into a toolbar.
31411  * @constructor
31412  * Creates a new TextItem
31413  * @cfg {string} text 
31414  */
31415 Roo.Toolbar.TextItem = function(cfg){
31416     var  text = cfg || "";
31417     if (typeof(cfg) == 'object') {
31418         text = cfg.text || "";
31419     }  else {
31420         cfg = null;
31421     }
31422     var s = document.createElement("span");
31423     s.className = "ytb-text";
31424     s.innerHTML = text;
31425     if (cfg) {
31426         cfg.el  = s;
31427     }
31428     
31429     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31430 };
31431 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31432     
31433      
31434     enable:Roo.emptyFn,
31435     disable:Roo.emptyFn,
31436     focus:Roo.emptyFn
31437 });
31438
31439 /**
31440  * @class Roo.Toolbar.Button
31441  * @extends Roo.Button
31442  * A button that renders into a toolbar.
31443  * @constructor
31444  * Creates a new Button
31445  * @param {Object} config A standard {@link Roo.Button} config object
31446  */
31447 Roo.Toolbar.Button = function(config){
31448     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31449 };
31450 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31451 {
31452     
31453     
31454     render : function(td){
31455         this.td = td;
31456         Roo.Toolbar.Button.superclass.render.call(this, td);
31457     },
31458     
31459     /**
31460      * Removes and destroys this button
31461      */
31462     destroy : function(){
31463         Roo.Toolbar.Button.superclass.destroy.call(this);
31464         this.td.parentNode.removeChild(this.td);
31465     },
31466     
31467     /**
31468      * Shows this button
31469      */
31470     show: function(){
31471         this.hidden = false;
31472         this.td.style.display = "";
31473     },
31474     
31475     /**
31476      * Hides this button
31477      */
31478     hide: function(){
31479         this.hidden = true;
31480         this.td.style.display = "none";
31481     },
31482
31483     /**
31484      * Disables this item
31485      */
31486     disable : function(){
31487         Roo.fly(this.td).addClass("x-item-disabled");
31488         this.disabled = true;
31489     },
31490
31491     /**
31492      * Enables this item
31493      */
31494     enable : function(){
31495         Roo.fly(this.td).removeClass("x-item-disabled");
31496         this.disabled = false;
31497     }
31498 });
31499 // backwards compat
31500 Roo.ToolbarButton = Roo.Toolbar.Button;
31501
31502 /**
31503  * @class Roo.Toolbar.SplitButton
31504  * @extends Roo.SplitButton
31505  * A menu button that renders into a toolbar.
31506  * @constructor
31507  * Creates a new SplitButton
31508  * @param {Object} config A standard {@link Roo.SplitButton} config object
31509  */
31510 Roo.Toolbar.SplitButton = function(config){
31511     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31512 };
31513 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31514     render : function(td){
31515         this.td = td;
31516         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31517     },
31518     
31519     /**
31520      * Removes and destroys this button
31521      */
31522     destroy : function(){
31523         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31524         this.td.parentNode.removeChild(this.td);
31525     },
31526     
31527     /**
31528      * Shows this button
31529      */
31530     show: function(){
31531         this.hidden = false;
31532         this.td.style.display = "";
31533     },
31534     
31535     /**
31536      * Hides this button
31537      */
31538     hide: function(){
31539         this.hidden = true;
31540         this.td.style.display = "none";
31541     }
31542 });
31543
31544 // backwards compat
31545 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31546  * Based on:
31547  * Ext JS Library 1.1.1
31548  * Copyright(c) 2006-2007, Ext JS, LLC.
31549  *
31550  * Originally Released Under LGPL - original licence link has changed is not relivant.
31551  *
31552  * Fork - LGPL
31553  * <script type="text/javascript">
31554  */
31555  
31556 /**
31557  * @class Roo.PagingToolbar
31558  * @extends Roo.Toolbar
31559  * @children   Roo.Toolbar.Item Roo.form.Field
31560  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31561  * @constructor
31562  * Create a new PagingToolbar
31563  * @param {Object} config The config object
31564  */
31565 Roo.PagingToolbar = function(el, ds, config)
31566 {
31567     // old args format still supported... - xtype is prefered..
31568     if (typeof(el) == 'object' && el.xtype) {
31569         // created from xtype...
31570         config = el;
31571         ds = el.dataSource;
31572         el = config.container;
31573     }
31574     var items = [];
31575     if (config.items) {
31576         items = config.items;
31577         config.items = [];
31578     }
31579     
31580     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31581     this.ds = ds;
31582     this.cursor = 0;
31583     this.renderButtons(this.el);
31584     this.bind(ds);
31585     
31586     // supprot items array.
31587    
31588     Roo.each(items, function(e) {
31589         this.add(Roo.factory(e));
31590     },this);
31591     
31592 };
31593
31594 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31595    
31596     /**
31597      * @cfg {String/HTMLElement/Element} container
31598      * container The id or element that will contain the toolbar
31599      */
31600     /**
31601      * @cfg {Boolean} displayInfo
31602      * True to display the displayMsg (defaults to false)
31603      */
31604     
31605     
31606     /**
31607      * @cfg {Number} pageSize
31608      * The number of records to display per page (defaults to 20)
31609      */
31610     pageSize: 20,
31611     /**
31612      * @cfg {String} displayMsg
31613      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31614      */
31615     displayMsg : 'Displaying {0} - {1} of {2}',
31616     /**
31617      * @cfg {String} emptyMsg
31618      * The message to display when no records are found (defaults to "No data to display")
31619      */
31620     emptyMsg : 'No data to display',
31621     /**
31622      * Customizable piece of the default paging text (defaults to "Page")
31623      * @type String
31624      */
31625     beforePageText : "Page",
31626     /**
31627      * Customizable piece of the default paging text (defaults to "of %0")
31628      * @type String
31629      */
31630     afterPageText : "of {0}",
31631     /**
31632      * Customizable piece of the default paging text (defaults to "First Page")
31633      * @type String
31634      */
31635     firstText : "First Page",
31636     /**
31637      * Customizable piece of the default paging text (defaults to "Previous Page")
31638      * @type String
31639      */
31640     prevText : "Previous Page",
31641     /**
31642      * Customizable piece of the default paging text (defaults to "Next Page")
31643      * @type String
31644      */
31645     nextText : "Next Page",
31646     /**
31647      * Customizable piece of the default paging text (defaults to "Last Page")
31648      * @type String
31649      */
31650     lastText : "Last Page",
31651     /**
31652      * Customizable piece of the default paging text (defaults to "Refresh")
31653      * @type String
31654      */
31655     refreshText : "Refresh",
31656
31657     // private
31658     renderButtons : function(el){
31659         Roo.PagingToolbar.superclass.render.call(this, el);
31660         this.first = this.addButton({
31661             tooltip: this.firstText,
31662             cls: "x-btn-icon x-grid-page-first",
31663             disabled: true,
31664             handler: this.onClick.createDelegate(this, ["first"])
31665         });
31666         this.prev = this.addButton({
31667             tooltip: this.prevText,
31668             cls: "x-btn-icon x-grid-page-prev",
31669             disabled: true,
31670             handler: this.onClick.createDelegate(this, ["prev"])
31671         });
31672         //this.addSeparator();
31673         this.add(this.beforePageText);
31674         this.field = Roo.get(this.addDom({
31675            tag: "input",
31676            type: "text",
31677            size: "3",
31678            value: "1",
31679            cls: "x-grid-page-number"
31680         }).el);
31681         this.field.on("keydown", this.onPagingKeydown, this);
31682         this.field.on("focus", function(){this.dom.select();});
31683         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31684         this.field.setHeight(18);
31685         //this.addSeparator();
31686         this.next = this.addButton({
31687             tooltip: this.nextText,
31688             cls: "x-btn-icon x-grid-page-next",
31689             disabled: true,
31690             handler: this.onClick.createDelegate(this, ["next"])
31691         });
31692         this.last = this.addButton({
31693             tooltip: this.lastText,
31694             cls: "x-btn-icon x-grid-page-last",
31695             disabled: true,
31696             handler: this.onClick.createDelegate(this, ["last"])
31697         });
31698         //this.addSeparator();
31699         this.loading = this.addButton({
31700             tooltip: this.refreshText,
31701             cls: "x-btn-icon x-grid-loading",
31702             handler: this.onClick.createDelegate(this, ["refresh"])
31703         });
31704
31705         if(this.displayInfo){
31706             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31707         }
31708     },
31709
31710     // private
31711     updateInfo : function(){
31712         if(this.displayEl){
31713             var count = this.ds.getCount();
31714             var msg = count == 0 ?
31715                 this.emptyMsg :
31716                 String.format(
31717                     this.displayMsg,
31718                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31719                 );
31720             this.displayEl.update(msg);
31721         }
31722     },
31723
31724     // private
31725     onLoad : function(ds, r, o){
31726        this.cursor = o.params ? o.params.start : 0;
31727        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31728
31729        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31730        this.field.dom.value = ap;
31731        this.first.setDisabled(ap == 1);
31732        this.prev.setDisabled(ap == 1);
31733        this.next.setDisabled(ap == ps);
31734        this.last.setDisabled(ap == ps);
31735        this.loading.enable();
31736        this.updateInfo();
31737     },
31738
31739     // private
31740     getPageData : function(){
31741         var total = this.ds.getTotalCount();
31742         return {
31743             total : total,
31744             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31745             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31746         };
31747     },
31748
31749     // private
31750     onLoadError : function(){
31751         this.loading.enable();
31752     },
31753
31754     // private
31755     onPagingKeydown : function(e){
31756         var k = e.getKey();
31757         var d = this.getPageData();
31758         if(k == e.RETURN){
31759             var v = this.field.dom.value, pageNum;
31760             if(!v || isNaN(pageNum = parseInt(v, 10))){
31761                 this.field.dom.value = d.activePage;
31762                 return;
31763             }
31764             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31765             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31766             e.stopEvent();
31767         }
31768         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))
31769         {
31770           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31771           this.field.dom.value = pageNum;
31772           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31773           e.stopEvent();
31774         }
31775         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31776         {
31777           var v = this.field.dom.value, pageNum; 
31778           var increment = (e.shiftKey) ? 10 : 1;
31779           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31780             increment *= -1;
31781           }
31782           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31783             this.field.dom.value = d.activePage;
31784             return;
31785           }
31786           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31787           {
31788             this.field.dom.value = parseInt(v, 10) + increment;
31789             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31790             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31791           }
31792           e.stopEvent();
31793         }
31794     },
31795
31796     // private
31797     beforeLoad : function(){
31798         if(this.loading){
31799             this.loading.disable();
31800         }
31801     },
31802
31803     // private
31804     onClick : function(which){
31805         var ds = this.ds;
31806         switch(which){
31807             case "first":
31808                 ds.load({params:{start: 0, limit: this.pageSize}});
31809             break;
31810             case "prev":
31811                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31812             break;
31813             case "next":
31814                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31815             break;
31816             case "last":
31817                 var total = ds.getTotalCount();
31818                 var extra = total % this.pageSize;
31819                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31820                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31821             break;
31822             case "refresh":
31823                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31824             break;
31825         }
31826     },
31827
31828     /**
31829      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31830      * @param {Roo.data.Store} store The data store to unbind
31831      */
31832     unbind : function(ds){
31833         ds.un("beforeload", this.beforeLoad, this);
31834         ds.un("load", this.onLoad, this);
31835         ds.un("loadexception", this.onLoadError, this);
31836         ds.un("remove", this.updateInfo, this);
31837         ds.un("add", this.updateInfo, this);
31838         this.ds = undefined;
31839     },
31840
31841     /**
31842      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31843      * @param {Roo.data.Store} store The data store to bind
31844      */
31845     bind : function(ds){
31846         ds.on("beforeload", this.beforeLoad, this);
31847         ds.on("load", this.onLoad, this);
31848         ds.on("loadexception", this.onLoadError, this);
31849         ds.on("remove", this.updateInfo, this);
31850         ds.on("add", this.updateInfo, this);
31851         this.ds = ds;
31852     }
31853 });/*
31854  * Based on:
31855  * Ext JS Library 1.1.1
31856  * Copyright(c) 2006-2007, Ext JS, LLC.
31857  *
31858  * Originally Released Under LGPL - original licence link has changed is not relivant.
31859  *
31860  * Fork - LGPL
31861  * <script type="text/javascript">
31862  */
31863
31864 /**
31865  * @class Roo.Resizable
31866  * @extends Roo.util.Observable
31867  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31868  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31869  * 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
31870  * the element will be wrapped for you automatically.</p>
31871  * <p>Here is the list of valid resize handles:</p>
31872  * <pre>
31873 Value   Description
31874 ------  -------------------
31875  'n'     north
31876  's'     south
31877  'e'     east
31878  'w'     west
31879  'nw'    northwest
31880  'sw'    southwest
31881  'se'    southeast
31882  'ne'    northeast
31883  'hd'    horizontal drag
31884  'all'   all
31885 </pre>
31886  * <p>Here's an example showing the creation of a typical Resizable:</p>
31887  * <pre><code>
31888 var resizer = new Roo.Resizable("element-id", {
31889     handles: 'all',
31890     minWidth: 200,
31891     minHeight: 100,
31892     maxWidth: 500,
31893     maxHeight: 400,
31894     pinned: true
31895 });
31896 resizer.on("resize", myHandler);
31897 </code></pre>
31898  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31899  * resizer.east.setDisplayed(false);</p>
31900  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31901  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31902  * resize operation's new size (defaults to [0, 0])
31903  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31904  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31905  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31906  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31907  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31908  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31909  * @cfg {Number} width The width of the element in pixels (defaults to null)
31910  * @cfg {Number} height The height of the element in pixels (defaults to null)
31911  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31912  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31913  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31914  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31915  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31916  * in favor of the handles config option (defaults to false)
31917  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31918  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31919  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31920  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31921  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31922  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31923  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31924  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31925  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31926  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31927  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31928  * @constructor
31929  * Create a new resizable component
31930  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31931  * @param {Object} config configuration options
31932   */
31933 Roo.Resizable = function(el, config)
31934 {
31935     this.el = Roo.get(el);
31936
31937     if(config && config.wrap){
31938         config.resizeChild = this.el;
31939         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31940         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31941         this.el.setStyle("overflow", "hidden");
31942         this.el.setPositioning(config.resizeChild.getPositioning());
31943         config.resizeChild.clearPositioning();
31944         if(!config.width || !config.height){
31945             var csize = config.resizeChild.getSize();
31946             this.el.setSize(csize.width, csize.height);
31947         }
31948         if(config.pinned && !config.adjustments){
31949             config.adjustments = "auto";
31950         }
31951     }
31952
31953     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31954     this.proxy.unselectable();
31955     this.proxy.enableDisplayMode('block');
31956
31957     Roo.apply(this, config);
31958
31959     if(this.pinned){
31960         this.disableTrackOver = true;
31961         this.el.addClass("x-resizable-pinned");
31962     }
31963     // if the element isn't positioned, make it relative
31964     var position = this.el.getStyle("position");
31965     if(position != "absolute" && position != "fixed"){
31966         this.el.setStyle("position", "relative");
31967     }
31968     if(!this.handles){ // no handles passed, must be legacy style
31969         this.handles = 's,e,se';
31970         if(this.multiDirectional){
31971             this.handles += ',n,w';
31972         }
31973     }
31974     if(this.handles == "all"){
31975         this.handles = "n s e w ne nw se sw";
31976     }
31977     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31978     var ps = Roo.Resizable.positions;
31979     for(var i = 0, len = hs.length; i < len; i++){
31980         if(hs[i] && ps[hs[i]]){
31981             var pos = ps[hs[i]];
31982             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31983         }
31984     }
31985     // legacy
31986     this.corner = this.southeast;
31987     
31988     // updateBox = the box can move..
31989     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31990         this.updateBox = true;
31991     }
31992
31993     this.activeHandle = null;
31994
31995     if(this.resizeChild){
31996         if(typeof this.resizeChild == "boolean"){
31997             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31998         }else{
31999             this.resizeChild = Roo.get(this.resizeChild, true);
32000         }
32001     }
32002     
32003     if(this.adjustments == "auto"){
32004         var rc = this.resizeChild;
32005         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32006         if(rc && (hw || hn)){
32007             rc.position("relative");
32008             rc.setLeft(hw ? hw.el.getWidth() : 0);
32009             rc.setTop(hn ? hn.el.getHeight() : 0);
32010         }
32011         this.adjustments = [
32012             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32013             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32014         ];
32015     }
32016
32017     if(this.draggable){
32018         this.dd = this.dynamic ?
32019             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32020         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32021     }
32022
32023     // public events
32024     this.addEvents({
32025         /**
32026          * @event beforeresize
32027          * Fired before resize is allowed. Set enabled to false to cancel resize.
32028          * @param {Roo.Resizable} this
32029          * @param {Roo.EventObject} e The mousedown event
32030          */
32031         "beforeresize" : true,
32032         /**
32033          * @event resizing
32034          * Fired a resizing.
32035          * @param {Roo.Resizable} this
32036          * @param {Number} x The new x position
32037          * @param {Number} y The new y position
32038          * @param {Number} w The new w width
32039          * @param {Number} h The new h hight
32040          * @param {Roo.EventObject} e The mouseup event
32041          */
32042         "resizing" : true,
32043         /**
32044          * @event resize
32045          * Fired after a resize.
32046          * @param {Roo.Resizable} this
32047          * @param {Number} width The new width
32048          * @param {Number} height The new height
32049          * @param {Roo.EventObject} e The mouseup event
32050          */
32051         "resize" : true
32052     });
32053
32054     if(this.width !== null && this.height !== null){
32055         this.resizeTo(this.width, this.height);
32056     }else{
32057         this.updateChildSize();
32058     }
32059     if(Roo.isIE){
32060         this.el.dom.style.zoom = 1;
32061     }
32062     Roo.Resizable.superclass.constructor.call(this);
32063 };
32064
32065 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32066         resizeChild : false,
32067         adjustments : [0, 0],
32068         minWidth : 5,
32069         minHeight : 5,
32070         maxWidth : 10000,
32071         maxHeight : 10000,
32072         enabled : true,
32073         animate : false,
32074         duration : .35,
32075         dynamic : false,
32076         handles : false,
32077         multiDirectional : false,
32078         disableTrackOver : false,
32079         easing : 'easeOutStrong',
32080         widthIncrement : 0,
32081         heightIncrement : 0,
32082         pinned : false,
32083         width : null,
32084         height : null,
32085         preserveRatio : false,
32086         transparent: false,
32087         minX: 0,
32088         minY: 0,
32089         draggable: false,
32090
32091         /**
32092          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32093          */
32094         constrainTo: undefined,
32095         /**
32096          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32097          */
32098         resizeRegion: undefined,
32099
32100
32101     /**
32102      * Perform a manual resize
32103      * @param {Number} width
32104      * @param {Number} height
32105      */
32106     resizeTo : function(width, height){
32107         this.el.setSize(width, height);
32108         this.updateChildSize();
32109         this.fireEvent("resize", this, width, height, null);
32110     },
32111
32112     // private
32113     startSizing : function(e, handle){
32114         this.fireEvent("beforeresize", this, e);
32115         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32116
32117             if(!this.overlay){
32118                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32119                 this.overlay.unselectable();
32120                 this.overlay.enableDisplayMode("block");
32121                 this.overlay.on("mousemove", this.onMouseMove, this);
32122                 this.overlay.on("mouseup", this.onMouseUp, this);
32123             }
32124             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32125
32126             this.resizing = true;
32127             this.startBox = this.el.getBox();
32128             this.startPoint = e.getXY();
32129             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32130                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32131
32132             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32133             this.overlay.show();
32134
32135             if(this.constrainTo) {
32136                 var ct = Roo.get(this.constrainTo);
32137                 this.resizeRegion = ct.getRegion().adjust(
32138                     ct.getFrameWidth('t'),
32139                     ct.getFrameWidth('l'),
32140                     -ct.getFrameWidth('b'),
32141                     -ct.getFrameWidth('r')
32142                 );
32143             }
32144
32145             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32146             this.proxy.show();
32147             this.proxy.setBox(this.startBox);
32148             if(!this.dynamic){
32149                 this.proxy.setStyle('visibility', 'visible');
32150             }
32151         }
32152     },
32153
32154     // private
32155     onMouseDown : function(handle, e){
32156         if(this.enabled){
32157             e.stopEvent();
32158             this.activeHandle = handle;
32159             this.startSizing(e, handle);
32160         }
32161     },
32162
32163     // private
32164     onMouseUp : function(e){
32165         var size = this.resizeElement();
32166         this.resizing = false;
32167         this.handleOut();
32168         this.overlay.hide();
32169         this.proxy.hide();
32170         this.fireEvent("resize", this, size.width, size.height, e);
32171     },
32172
32173     // private
32174     updateChildSize : function(){
32175         
32176         if(this.resizeChild){
32177             var el = this.el;
32178             var child = this.resizeChild;
32179             var adj = this.adjustments;
32180             if(el.dom.offsetWidth){
32181                 var b = el.getSize(true);
32182                 child.setSize(b.width+adj[0], b.height+adj[1]);
32183             }
32184             // Second call here for IE
32185             // The first call enables instant resizing and
32186             // the second call corrects scroll bars if they
32187             // exist
32188             if(Roo.isIE){
32189                 setTimeout(function(){
32190                     if(el.dom.offsetWidth){
32191                         var b = el.getSize(true);
32192                         child.setSize(b.width+adj[0], b.height+adj[1]);
32193                     }
32194                 }, 10);
32195             }
32196         }
32197     },
32198
32199     // private
32200     snap : function(value, inc, min){
32201         if(!inc || !value) {
32202             return value;
32203         }
32204         var newValue = value;
32205         var m = value % inc;
32206         if(m > 0){
32207             if(m > (inc/2)){
32208                 newValue = value + (inc-m);
32209             }else{
32210                 newValue = value - m;
32211             }
32212         }
32213         return Math.max(min, newValue);
32214     },
32215
32216     // private
32217     resizeElement : function(){
32218         var box = this.proxy.getBox();
32219         if(this.updateBox){
32220             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32221         }else{
32222             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32223         }
32224         this.updateChildSize();
32225         if(!this.dynamic){
32226             this.proxy.hide();
32227         }
32228         return box;
32229     },
32230
32231     // private
32232     constrain : function(v, diff, m, mx){
32233         if(v - diff < m){
32234             diff = v - m;
32235         }else if(v - diff > mx){
32236             diff = mx - v;
32237         }
32238         return diff;
32239     },
32240
32241     // private
32242     onMouseMove : function(e){
32243         
32244         if(this.enabled){
32245             try{// try catch so if something goes wrong the user doesn't get hung
32246
32247             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32248                 return;
32249             }
32250
32251             //var curXY = this.startPoint;
32252             var curSize = this.curSize || this.startBox;
32253             var x = this.startBox.x, y = this.startBox.y;
32254             var ox = x, oy = y;
32255             var w = curSize.width, h = curSize.height;
32256             var ow = w, oh = h;
32257             var mw = this.minWidth, mh = this.minHeight;
32258             var mxw = this.maxWidth, mxh = this.maxHeight;
32259             var wi = this.widthIncrement;
32260             var hi = this.heightIncrement;
32261
32262             var eventXY = e.getXY();
32263             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32264             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32265
32266             var pos = this.activeHandle.position;
32267
32268             switch(pos){
32269                 case "east":
32270                     w += diffX;
32271                     w = Math.min(Math.max(mw, w), mxw);
32272                     break;
32273              
32274                 case "south":
32275                     h += diffY;
32276                     h = Math.min(Math.max(mh, h), mxh);
32277                     break;
32278                 case "southeast":
32279                     w += diffX;
32280                     h += diffY;
32281                     w = Math.min(Math.max(mw, w), mxw);
32282                     h = Math.min(Math.max(mh, h), mxh);
32283                     break;
32284                 case "north":
32285                     diffY = this.constrain(h, diffY, mh, mxh);
32286                     y += diffY;
32287                     h -= diffY;
32288                     break;
32289                 case "hdrag":
32290                     
32291                     if (wi) {
32292                         var adiffX = Math.abs(diffX);
32293                         var sub = (adiffX % wi); // how much 
32294                         if (sub > (wi/2)) { // far enough to snap
32295                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32296                         } else {
32297                             // remove difference.. 
32298                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32299                         }
32300                     }
32301                     x += diffX;
32302                     x = Math.max(this.minX, x);
32303                     break;
32304                 case "west":
32305                     diffX = this.constrain(w, diffX, mw, mxw);
32306                     x += diffX;
32307                     w -= diffX;
32308                     break;
32309                 case "northeast":
32310                     w += diffX;
32311                     w = Math.min(Math.max(mw, w), mxw);
32312                     diffY = this.constrain(h, diffY, mh, mxh);
32313                     y += diffY;
32314                     h -= diffY;
32315                     break;
32316                 case "northwest":
32317                     diffX = this.constrain(w, diffX, mw, mxw);
32318                     diffY = this.constrain(h, diffY, mh, mxh);
32319                     y += diffY;
32320                     h -= diffY;
32321                     x += diffX;
32322                     w -= diffX;
32323                     break;
32324                case "southwest":
32325                     diffX = this.constrain(w, diffX, mw, mxw);
32326                     h += diffY;
32327                     h = Math.min(Math.max(mh, h), mxh);
32328                     x += diffX;
32329                     w -= diffX;
32330                     break;
32331             }
32332
32333             var sw = this.snap(w, wi, mw);
32334             var sh = this.snap(h, hi, mh);
32335             if(sw != w || sh != h){
32336                 switch(pos){
32337                     case "northeast":
32338                         y -= sh - h;
32339                     break;
32340                     case "north":
32341                         y -= sh - h;
32342                         break;
32343                     case "southwest":
32344                         x -= sw - w;
32345                     break;
32346                     case "west":
32347                         x -= sw - w;
32348                         break;
32349                     case "northwest":
32350                         x -= sw - w;
32351                         y -= sh - h;
32352                     break;
32353                 }
32354                 w = sw;
32355                 h = sh;
32356             }
32357
32358             if(this.preserveRatio){
32359                 switch(pos){
32360                     case "southeast":
32361                     case "east":
32362                         h = oh * (w/ow);
32363                         h = Math.min(Math.max(mh, h), mxh);
32364                         w = ow * (h/oh);
32365                        break;
32366                     case "south":
32367                         w = ow * (h/oh);
32368                         w = Math.min(Math.max(mw, w), mxw);
32369                         h = oh * (w/ow);
32370                         break;
32371                     case "northeast":
32372                         w = ow * (h/oh);
32373                         w = Math.min(Math.max(mw, w), mxw);
32374                         h = oh * (w/ow);
32375                     break;
32376                     case "north":
32377                         var tw = w;
32378                         w = ow * (h/oh);
32379                         w = Math.min(Math.max(mw, w), mxw);
32380                         h = oh * (w/ow);
32381                         x += (tw - w) / 2;
32382                         break;
32383                     case "southwest":
32384                         h = oh * (w/ow);
32385                         h = Math.min(Math.max(mh, h), mxh);
32386                         var tw = w;
32387                         w = ow * (h/oh);
32388                         x += tw - w;
32389                         break;
32390                     case "west":
32391                         var th = h;
32392                         h = oh * (w/ow);
32393                         h = Math.min(Math.max(mh, h), mxh);
32394                         y += (th - h) / 2;
32395                         var tw = w;
32396                         w = ow * (h/oh);
32397                         x += tw - w;
32398                        break;
32399                     case "northwest":
32400                         var tw = w;
32401                         var th = h;
32402                         h = oh * (w/ow);
32403                         h = Math.min(Math.max(mh, h), mxh);
32404                         w = ow * (h/oh);
32405                         y += th - h;
32406                         x += tw - w;
32407                        break;
32408
32409                 }
32410             }
32411             if (pos == 'hdrag') {
32412                 w = ow;
32413             }
32414             this.proxy.setBounds(x, y, w, h);
32415             if(this.dynamic){
32416                 this.resizeElement();
32417             }
32418             }catch(e){}
32419         }
32420         this.fireEvent("resizing", this, x, y, w, h, e);
32421     },
32422
32423     // private
32424     handleOver : function(){
32425         if(this.enabled){
32426             this.el.addClass("x-resizable-over");
32427         }
32428     },
32429
32430     // private
32431     handleOut : function(){
32432         if(!this.resizing){
32433             this.el.removeClass("x-resizable-over");
32434         }
32435     },
32436
32437     /**
32438      * Returns the element this component is bound to.
32439      * @return {Roo.Element}
32440      */
32441     getEl : function(){
32442         return this.el;
32443     },
32444
32445     /**
32446      * Returns the resizeChild element (or null).
32447      * @return {Roo.Element}
32448      */
32449     getResizeChild : function(){
32450         return this.resizeChild;
32451     },
32452     groupHandler : function()
32453     {
32454         
32455     },
32456     /**
32457      * Destroys this resizable. If the element was wrapped and
32458      * removeEl is not true then the element remains.
32459      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32460      */
32461     destroy : function(removeEl){
32462         this.proxy.remove();
32463         if(this.overlay){
32464             this.overlay.removeAllListeners();
32465             this.overlay.remove();
32466         }
32467         var ps = Roo.Resizable.positions;
32468         for(var k in ps){
32469             if(typeof ps[k] != "function" && this[ps[k]]){
32470                 var h = this[ps[k]];
32471                 h.el.removeAllListeners();
32472                 h.el.remove();
32473             }
32474         }
32475         if(removeEl){
32476             this.el.update("");
32477             this.el.remove();
32478         }
32479     }
32480 });
32481
32482 // private
32483 // hash to map config positions to true positions
32484 Roo.Resizable.positions = {
32485     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32486     hd: "hdrag"
32487 };
32488
32489 // private
32490 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32491     if(!this.tpl){
32492         // only initialize the template if resizable is used
32493         var tpl = Roo.DomHelper.createTemplate(
32494             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32495         );
32496         tpl.compile();
32497         Roo.Resizable.Handle.prototype.tpl = tpl;
32498     }
32499     this.position = pos;
32500     this.rz = rz;
32501     // show north drag fro topdra
32502     var handlepos = pos == 'hdrag' ? 'north' : pos;
32503     
32504     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32505     if (pos == 'hdrag') {
32506         this.el.setStyle('cursor', 'pointer');
32507     }
32508     this.el.unselectable();
32509     if(transparent){
32510         this.el.setOpacity(0);
32511     }
32512     this.el.on("mousedown", this.onMouseDown, this);
32513     if(!disableTrackOver){
32514         this.el.on("mouseover", this.onMouseOver, this);
32515         this.el.on("mouseout", this.onMouseOut, this);
32516     }
32517 };
32518
32519 // private
32520 Roo.Resizable.Handle.prototype = {
32521     afterResize : function(rz){
32522         Roo.log('after?');
32523         // do nothing
32524     },
32525     // private
32526     onMouseDown : function(e){
32527         this.rz.onMouseDown(this, e);
32528     },
32529     // private
32530     onMouseOver : function(e){
32531         this.rz.handleOver(this, e);
32532     },
32533     // private
32534     onMouseOut : function(e){
32535         this.rz.handleOut(this, e);
32536     }
32537 };/*
32538  * Based on:
32539  * Ext JS Library 1.1.1
32540  * Copyright(c) 2006-2007, Ext JS, LLC.
32541  *
32542  * Originally Released Under LGPL - original licence link has changed is not relivant.
32543  *
32544  * Fork - LGPL
32545  * <script type="text/javascript">
32546  */
32547
32548 /**
32549  * @class Roo.Editor
32550  * @extends Roo.Component
32551  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32552  * @constructor
32553  * Create a new Editor
32554  * @param {Roo.form.Field} field The Field object (or descendant)
32555  * @param {Object} config The config object
32556  */
32557 Roo.Editor = function(field, config){
32558     Roo.Editor.superclass.constructor.call(this, config);
32559     this.field = field;
32560     this.addEvents({
32561         /**
32562              * @event beforestartedit
32563              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32564              * false from the handler of this event.
32565              * @param {Editor} this
32566              * @param {Roo.Element} boundEl The underlying element bound to this editor
32567              * @param {Mixed} value The field value being set
32568              */
32569         "beforestartedit" : true,
32570         /**
32571              * @event startedit
32572              * Fires when this editor is displayed
32573              * @param {Roo.Element} boundEl The underlying element bound to this editor
32574              * @param {Mixed} value The starting field value
32575              */
32576         "startedit" : true,
32577         /**
32578              * @event beforecomplete
32579              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32580              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32581              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32582              * event will not fire since no edit actually occurred.
32583              * @param {Editor} this
32584              * @param {Mixed} value The current field value
32585              * @param {Mixed} startValue The original field value
32586              */
32587         "beforecomplete" : true,
32588         /**
32589              * @event complete
32590              * Fires after editing is complete and any changed value has been written to the underlying field.
32591              * @param {Editor} this
32592              * @param {Mixed} value The current field value
32593              * @param {Mixed} startValue The original field value
32594              */
32595         "complete" : true,
32596         /**
32597          * @event specialkey
32598          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32599          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32600          * @param {Roo.form.Field} this
32601          * @param {Roo.EventObject} e The event object
32602          */
32603         "specialkey" : true
32604     });
32605 };
32606
32607 Roo.extend(Roo.Editor, Roo.Component, {
32608     /**
32609      * @cfg {Boolean/String} autosize
32610      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32611      * or "height" to adopt the height only (defaults to false)
32612      */
32613     /**
32614      * @cfg {Boolean} revertInvalid
32615      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32616      * validation fails (defaults to true)
32617      */
32618     /**
32619      * @cfg {Boolean} ignoreNoChange
32620      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32621      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32622      * will never be ignored.
32623      */
32624     /**
32625      * @cfg {Boolean} hideEl
32626      * False to keep the bound element visible while the editor is displayed (defaults to true)
32627      */
32628     /**
32629      * @cfg {Mixed} value
32630      * The data value of the underlying field (defaults to "")
32631      */
32632     value : "",
32633     /**
32634      * @cfg {String} alignment
32635      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32636      */
32637     alignment: "c-c?",
32638     /**
32639      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32640      * for bottom-right shadow (defaults to "frame")
32641      */
32642     shadow : "frame",
32643     /**
32644      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32645      */
32646     constrain : false,
32647     /**
32648      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32649      */
32650     completeOnEnter : false,
32651     /**
32652      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32653      */
32654     cancelOnEsc : false,
32655     /**
32656      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32657      */
32658     updateEl : false,
32659
32660     // private
32661     onRender : function(ct, position){
32662         this.el = new Roo.Layer({
32663             shadow: this.shadow,
32664             cls: "x-editor",
32665             parentEl : ct,
32666             shim : this.shim,
32667             shadowOffset:4,
32668             id: this.id,
32669             constrain: this.constrain
32670         });
32671         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32672         if(this.field.msgTarget != 'title'){
32673             this.field.msgTarget = 'qtip';
32674         }
32675         this.field.render(this.el);
32676         if(Roo.isGecko){
32677             this.field.el.dom.setAttribute('autocomplete', 'off');
32678         }
32679         this.field.on("specialkey", this.onSpecialKey, this);
32680         if(this.swallowKeys){
32681             this.field.el.swallowEvent(['keydown','keypress']);
32682         }
32683         this.field.show();
32684         this.field.on("blur", this.onBlur, this);
32685         if(this.field.grow){
32686             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32687         }
32688     },
32689
32690     onSpecialKey : function(field, e)
32691     {
32692         //Roo.log('editor onSpecialKey');
32693         if(this.completeOnEnter && e.getKey() == e.ENTER){
32694             e.stopEvent();
32695             this.completeEdit();
32696             return;
32697         }
32698         // do not fire special key otherwise it might hide close the editor...
32699         if(e.getKey() == e.ENTER){    
32700             return;
32701         }
32702         if(this.cancelOnEsc && e.getKey() == e.ESC){
32703             this.cancelEdit();
32704             return;
32705         } 
32706         this.fireEvent('specialkey', field, e);
32707     
32708     },
32709
32710     /**
32711      * Starts the editing process and shows the editor.
32712      * @param {String/HTMLElement/Element} el The element to edit
32713      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32714       * to the innerHTML of el.
32715      */
32716     startEdit : function(el, value){
32717         if(this.editing){
32718             this.completeEdit();
32719         }
32720         this.boundEl = Roo.get(el);
32721         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32722         if(!this.rendered){
32723             this.render(this.parentEl || document.body);
32724         }
32725         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32726             return;
32727         }
32728         this.startValue = v;
32729         this.field.setValue(v);
32730         if(this.autoSize){
32731             var sz = this.boundEl.getSize();
32732             switch(this.autoSize){
32733                 case "width":
32734                 this.setSize(sz.width,  "");
32735                 break;
32736                 case "height":
32737                 this.setSize("",  sz.height);
32738                 break;
32739                 default:
32740                 this.setSize(sz.width,  sz.height);
32741             }
32742         }
32743         this.el.alignTo(this.boundEl, this.alignment);
32744         this.editing = true;
32745         if(Roo.QuickTips){
32746             Roo.QuickTips.disable();
32747         }
32748         this.show();
32749     },
32750
32751     /**
32752      * Sets the height and width of this editor.
32753      * @param {Number} width The new width
32754      * @param {Number} height The new height
32755      */
32756     setSize : function(w, h){
32757         this.field.setSize(w, h);
32758         if(this.el){
32759             this.el.sync();
32760         }
32761     },
32762
32763     /**
32764      * Realigns the editor to the bound field based on the current alignment config value.
32765      */
32766     realign : function(){
32767         this.el.alignTo(this.boundEl, this.alignment);
32768     },
32769
32770     /**
32771      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32772      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32773      */
32774     completeEdit : function(remainVisible){
32775         if(!this.editing){
32776             return;
32777         }
32778         var v = this.getValue();
32779         if(this.revertInvalid !== false && !this.field.isValid()){
32780             v = this.startValue;
32781             this.cancelEdit(true);
32782         }
32783         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32784             this.editing = false;
32785             this.hide();
32786             return;
32787         }
32788         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32789             this.editing = false;
32790             if(this.updateEl && this.boundEl){
32791                 this.boundEl.update(v);
32792             }
32793             if(remainVisible !== true){
32794                 this.hide();
32795             }
32796             this.fireEvent("complete", this, v, this.startValue);
32797         }
32798     },
32799
32800     // private
32801     onShow : function(){
32802         this.el.show();
32803         if(this.hideEl !== false){
32804             this.boundEl.hide();
32805         }
32806         this.field.show();
32807         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32808             this.fixIEFocus = true;
32809             this.deferredFocus.defer(50, this);
32810         }else{
32811             this.field.focus();
32812         }
32813         this.fireEvent("startedit", this.boundEl, this.startValue);
32814     },
32815
32816     deferredFocus : function(){
32817         if(this.editing){
32818             this.field.focus();
32819         }
32820     },
32821
32822     /**
32823      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32824      * reverted to the original starting value.
32825      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32826      * cancel (defaults to false)
32827      */
32828     cancelEdit : function(remainVisible){
32829         if(this.editing){
32830             this.setValue(this.startValue);
32831             if(remainVisible !== true){
32832                 this.hide();
32833             }
32834         }
32835     },
32836
32837     // private
32838     onBlur : function(){
32839         if(this.allowBlur !== true && this.editing){
32840             this.completeEdit();
32841         }
32842     },
32843
32844     // private
32845     onHide : function(){
32846         if(this.editing){
32847             this.completeEdit();
32848             return;
32849         }
32850         this.field.blur();
32851         if(this.field.collapse){
32852             this.field.collapse();
32853         }
32854         this.el.hide();
32855         if(this.hideEl !== false){
32856             this.boundEl.show();
32857         }
32858         if(Roo.QuickTips){
32859             Roo.QuickTips.enable();
32860         }
32861     },
32862
32863     /**
32864      * Sets the data value of the editor
32865      * @param {Mixed} value Any valid value supported by the underlying field
32866      */
32867     setValue : function(v){
32868         this.field.setValue(v);
32869     },
32870
32871     /**
32872      * Gets the data value of the editor
32873      * @return {Mixed} The data value
32874      */
32875     getValue : function(){
32876         return this.field.getValue();
32877     }
32878 });/*
32879  * Based on:
32880  * Ext JS Library 1.1.1
32881  * Copyright(c) 2006-2007, Ext JS, LLC.
32882  *
32883  * Originally Released Under LGPL - original licence link has changed is not relivant.
32884  *
32885  * Fork - LGPL
32886  * <script type="text/javascript">
32887  */
32888  
32889 /**
32890  * @class Roo.BasicDialog
32891  * @extends Roo.util.Observable
32892  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32893  * <pre><code>
32894 var dlg = new Roo.BasicDialog("my-dlg", {
32895     height: 200,
32896     width: 300,
32897     minHeight: 100,
32898     minWidth: 150,
32899     modal: true,
32900     proxyDrag: true,
32901     shadow: true
32902 });
32903 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32904 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32905 dlg.addButton('Cancel', dlg.hide, dlg);
32906 dlg.show();
32907 </code></pre>
32908   <b>A Dialog should always be a direct child of the body element.</b>
32909  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32910  * @cfg {String} title Default text to display in the title bar (defaults to null)
32911  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32912  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32913  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32914  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32915  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32916  * (defaults to null with no animation)
32917  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32918  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32919  * property for valid values (defaults to 'all')
32920  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32921  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32922  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32923  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32924  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32925  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32926  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32927  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32928  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32929  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32930  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32931  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32932  * draggable = true (defaults to false)
32933  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32934  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32935  * shadow (defaults to false)
32936  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32937  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32938  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32939  * @cfg {Array} buttons Array of buttons
32940  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32941  * @constructor
32942  * Create a new BasicDialog.
32943  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32944  * @param {Object} config Configuration options
32945  */
32946 Roo.BasicDialog = function(el, config){
32947     this.el = Roo.get(el);
32948     var dh = Roo.DomHelper;
32949     if(!this.el && config && config.autoCreate){
32950         if(typeof config.autoCreate == "object"){
32951             if(!config.autoCreate.id){
32952                 config.autoCreate.id = el;
32953             }
32954             this.el = dh.append(document.body,
32955                         config.autoCreate, true);
32956         }else{
32957             this.el = dh.append(document.body,
32958                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32959         }
32960     }
32961     el = this.el;
32962     el.setDisplayed(true);
32963     el.hide = this.hideAction;
32964     this.id = el.id;
32965     el.addClass("x-dlg");
32966
32967     Roo.apply(this, config);
32968
32969     this.proxy = el.createProxy("x-dlg-proxy");
32970     this.proxy.hide = this.hideAction;
32971     this.proxy.setOpacity(.5);
32972     this.proxy.hide();
32973
32974     if(config.width){
32975         el.setWidth(config.width);
32976     }
32977     if(config.height){
32978         el.setHeight(config.height);
32979     }
32980     this.size = el.getSize();
32981     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32982         this.xy = [config.x,config.y];
32983     }else{
32984         this.xy = el.getCenterXY(true);
32985     }
32986     /** The header element @type Roo.Element */
32987     this.header = el.child("> .x-dlg-hd");
32988     /** The body element @type Roo.Element */
32989     this.body = el.child("> .x-dlg-bd");
32990     /** The footer element @type Roo.Element */
32991     this.footer = el.child("> .x-dlg-ft");
32992
32993     if(!this.header){
32994         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32995     }
32996     if(!this.body){
32997         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32998     }
32999
33000     this.header.unselectable();
33001     if(this.title){
33002         this.header.update(this.title);
33003     }
33004     // this element allows the dialog to be focused for keyboard event
33005     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33006     this.focusEl.swallowEvent("click", true);
33007
33008     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33009
33010     // wrap the body and footer for special rendering
33011     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33012     if(this.footer){
33013         this.bwrap.dom.appendChild(this.footer.dom);
33014     }
33015
33016     this.bg = this.el.createChild({
33017         tag: "div", cls:"x-dlg-bg",
33018         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33019     });
33020     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33021
33022
33023     if(this.autoScroll !== false && !this.autoTabs){
33024         this.body.setStyle("overflow", "auto");
33025     }
33026
33027     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33028
33029     if(this.closable !== false){
33030         this.el.addClass("x-dlg-closable");
33031         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33032         this.close.on("click", this.closeClick, this);
33033         this.close.addClassOnOver("x-dlg-close-over");
33034     }
33035     if(this.collapsible !== false){
33036         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33037         this.collapseBtn.on("click", this.collapseClick, this);
33038         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33039         this.header.on("dblclick", this.collapseClick, this);
33040     }
33041     if(this.resizable !== false){
33042         this.el.addClass("x-dlg-resizable");
33043         this.resizer = new Roo.Resizable(el, {
33044             minWidth: this.minWidth || 80,
33045             minHeight:this.minHeight || 80,
33046             handles: this.resizeHandles || "all",
33047             pinned: true
33048         });
33049         this.resizer.on("beforeresize", this.beforeResize, this);
33050         this.resizer.on("resize", this.onResize, this);
33051     }
33052     if(this.draggable !== false){
33053         el.addClass("x-dlg-draggable");
33054         if (!this.proxyDrag) {
33055             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33056         }
33057         else {
33058             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33059         }
33060         dd.setHandleElId(this.header.id);
33061         dd.endDrag = this.endMove.createDelegate(this);
33062         dd.startDrag = this.startMove.createDelegate(this);
33063         dd.onDrag = this.onDrag.createDelegate(this);
33064         dd.scroll = false;
33065         this.dd = dd;
33066     }
33067     if(this.modal){
33068         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33069         this.mask.enableDisplayMode("block");
33070         this.mask.hide();
33071         this.el.addClass("x-dlg-modal");
33072     }
33073     if(this.shadow){
33074         this.shadow = new Roo.Shadow({
33075             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33076             offset : this.shadowOffset
33077         });
33078     }else{
33079         this.shadowOffset = 0;
33080     }
33081     if(Roo.useShims && this.shim !== false){
33082         this.shim = this.el.createShim();
33083         this.shim.hide = this.hideAction;
33084         this.shim.hide();
33085     }else{
33086         this.shim = false;
33087     }
33088     if(this.autoTabs){
33089         this.initTabs();
33090     }
33091     if (this.buttons) { 
33092         var bts= this.buttons;
33093         this.buttons = [];
33094         Roo.each(bts, function(b) {
33095             this.addButton(b);
33096         }, this);
33097     }
33098     
33099     
33100     this.addEvents({
33101         /**
33102          * @event keydown
33103          * Fires when a key is pressed
33104          * @param {Roo.BasicDialog} this
33105          * @param {Roo.EventObject} e
33106          */
33107         "keydown" : true,
33108         /**
33109          * @event move
33110          * Fires when this dialog is moved by the user.
33111          * @param {Roo.BasicDialog} this
33112          * @param {Number} x The new page X
33113          * @param {Number} y The new page Y
33114          */
33115         "move" : true,
33116         /**
33117          * @event resize
33118          * Fires when this dialog is resized by the user.
33119          * @param {Roo.BasicDialog} this
33120          * @param {Number} width The new width
33121          * @param {Number} height The new height
33122          */
33123         "resize" : true,
33124         /**
33125          * @event beforehide
33126          * Fires before this dialog is hidden.
33127          * @param {Roo.BasicDialog} this
33128          */
33129         "beforehide" : true,
33130         /**
33131          * @event hide
33132          * Fires when this dialog is hidden.
33133          * @param {Roo.BasicDialog} this
33134          */
33135         "hide" : true,
33136         /**
33137          * @event beforeshow
33138          * Fires before this dialog is shown.
33139          * @param {Roo.BasicDialog} this
33140          */
33141         "beforeshow" : true,
33142         /**
33143          * @event show
33144          * Fires when this dialog is shown.
33145          * @param {Roo.BasicDialog} this
33146          */
33147         "show" : true
33148     });
33149     el.on("keydown", this.onKeyDown, this);
33150     el.on("mousedown", this.toFront, this);
33151     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33152     this.el.hide();
33153     Roo.DialogManager.register(this);
33154     Roo.BasicDialog.superclass.constructor.call(this);
33155 };
33156
33157 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33158     shadowOffset: Roo.isIE ? 6 : 5,
33159     minHeight: 80,
33160     minWidth: 200,
33161     minButtonWidth: 75,
33162     defaultButton: null,
33163     buttonAlign: "right",
33164     tabTag: 'div',
33165     firstShow: true,
33166
33167     /**
33168      * Sets the dialog title text
33169      * @param {String} text The title text to display
33170      * @return {Roo.BasicDialog} this
33171      */
33172     setTitle : function(text){
33173         this.header.update(text);
33174         return this;
33175     },
33176
33177     // private
33178     closeClick : function(){
33179         this.hide();
33180     },
33181
33182     // private
33183     collapseClick : function(){
33184         this[this.collapsed ? "expand" : "collapse"]();
33185     },
33186
33187     /**
33188      * Collapses the dialog to its minimized state (only the title bar is visible).
33189      * Equivalent to the user clicking the collapse dialog button.
33190      */
33191     collapse : function(){
33192         if(!this.collapsed){
33193             this.collapsed = true;
33194             this.el.addClass("x-dlg-collapsed");
33195             this.restoreHeight = this.el.getHeight();
33196             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33197         }
33198     },
33199
33200     /**
33201      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33202      * clicking the expand dialog button.
33203      */
33204     expand : function(){
33205         if(this.collapsed){
33206             this.collapsed = false;
33207             this.el.removeClass("x-dlg-collapsed");
33208             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33209         }
33210     },
33211
33212     /**
33213      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33214      * @return {Roo.TabPanel} The tabs component
33215      */
33216     initTabs : function(){
33217         var tabs = this.getTabs();
33218         while(tabs.getTab(0)){
33219             tabs.removeTab(0);
33220         }
33221         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33222             var dom = el.dom;
33223             tabs.addTab(Roo.id(dom), dom.title);
33224             dom.title = "";
33225         });
33226         tabs.activate(0);
33227         return tabs;
33228     },
33229
33230     // private
33231     beforeResize : function(){
33232         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33233     },
33234
33235     // private
33236     onResize : function(){
33237         this.refreshSize();
33238         this.syncBodyHeight();
33239         this.adjustAssets();
33240         this.focus();
33241         this.fireEvent("resize", this, this.size.width, this.size.height);
33242     },
33243
33244     // private
33245     onKeyDown : function(e){
33246         if(this.isVisible()){
33247             this.fireEvent("keydown", this, e);
33248         }
33249     },
33250
33251     /**
33252      * Resizes the dialog.
33253      * @param {Number} width
33254      * @param {Number} height
33255      * @return {Roo.BasicDialog} this
33256      */
33257     resizeTo : function(width, height){
33258         this.el.setSize(width, height);
33259         this.size = {width: width, height: height};
33260         this.syncBodyHeight();
33261         if(this.fixedcenter){
33262             this.center();
33263         }
33264         if(this.isVisible()){
33265             this.constrainXY();
33266             this.adjustAssets();
33267         }
33268         this.fireEvent("resize", this, width, height);
33269         return this;
33270     },
33271
33272
33273     /**
33274      * Resizes the dialog to fit the specified content size.
33275      * @param {Number} width
33276      * @param {Number} height
33277      * @return {Roo.BasicDialog} this
33278      */
33279     setContentSize : function(w, h){
33280         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33281         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33282         //if(!this.el.isBorderBox()){
33283             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33284             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33285         //}
33286         if(this.tabs){
33287             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33288             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33289         }
33290         this.resizeTo(w, h);
33291         return this;
33292     },
33293
33294     /**
33295      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33296      * executed in response to a particular key being pressed while the dialog is active.
33297      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33298      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33299      * @param {Function} fn The function to call
33300      * @param {Object} scope (optional) The scope of the function
33301      * @return {Roo.BasicDialog} this
33302      */
33303     addKeyListener : function(key, fn, scope){
33304         var keyCode, shift, ctrl, alt;
33305         if(typeof key == "object" && !(key instanceof Array)){
33306             keyCode = key["key"];
33307             shift = key["shift"];
33308             ctrl = key["ctrl"];
33309             alt = key["alt"];
33310         }else{
33311             keyCode = key;
33312         }
33313         var handler = function(dlg, e){
33314             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33315                 var k = e.getKey();
33316                 if(keyCode instanceof Array){
33317                     for(var i = 0, len = keyCode.length; i < len; i++){
33318                         if(keyCode[i] == k){
33319                           fn.call(scope || window, dlg, k, e);
33320                           return;
33321                         }
33322                     }
33323                 }else{
33324                     if(k == keyCode){
33325                         fn.call(scope || window, dlg, k, e);
33326                     }
33327                 }
33328             }
33329         };
33330         this.on("keydown", handler);
33331         return this;
33332     },
33333
33334     /**
33335      * Returns the TabPanel component (creates it if it doesn't exist).
33336      * Note: If you wish to simply check for the existence of tabs without creating them,
33337      * check for a null 'tabs' property.
33338      * @return {Roo.TabPanel} The tabs component
33339      */
33340     getTabs : function(){
33341         if(!this.tabs){
33342             this.el.addClass("x-dlg-auto-tabs");
33343             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33344             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33345         }
33346         return this.tabs;
33347     },
33348
33349     /**
33350      * Adds a button to the footer section of the dialog.
33351      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33352      * object or a valid Roo.DomHelper element config
33353      * @param {Function} handler The function called when the button is clicked
33354      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33355      * @return {Roo.Button} The new button
33356      */
33357     addButton : function(config, handler, scope){
33358         var dh = Roo.DomHelper;
33359         if(!this.footer){
33360             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33361         }
33362         if(!this.btnContainer){
33363             var tb = this.footer.createChild({
33364
33365                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33366                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33367             }, null, true);
33368             this.btnContainer = tb.firstChild.firstChild.firstChild;
33369         }
33370         var bconfig = {
33371             handler: handler,
33372             scope: scope,
33373             minWidth: this.minButtonWidth,
33374             hideParent:true
33375         };
33376         if(typeof config == "string"){
33377             bconfig.text = config;
33378         }else{
33379             if(config.tag){
33380                 bconfig.dhconfig = config;
33381             }else{
33382                 Roo.apply(bconfig, config);
33383             }
33384         }
33385         var fc = false;
33386         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33387             bconfig.position = Math.max(0, bconfig.position);
33388             fc = this.btnContainer.childNodes[bconfig.position];
33389         }
33390          
33391         var btn = new Roo.Button(
33392             fc ? 
33393                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33394                 : this.btnContainer.appendChild(document.createElement("td")),
33395             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33396             bconfig
33397         );
33398         this.syncBodyHeight();
33399         if(!this.buttons){
33400             /**
33401              * Array of all the buttons that have been added to this dialog via addButton
33402              * @type Array
33403              */
33404             this.buttons = [];
33405         }
33406         this.buttons.push(btn);
33407         return btn;
33408     },
33409
33410     /**
33411      * Sets the default button to be focused when the dialog is displayed.
33412      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33413      * @return {Roo.BasicDialog} this
33414      */
33415     setDefaultButton : function(btn){
33416         this.defaultButton = btn;
33417         return this;
33418     },
33419
33420     // private
33421     getHeaderFooterHeight : function(safe){
33422         var height = 0;
33423         if(this.header){
33424            height += this.header.getHeight();
33425         }
33426         if(this.footer){
33427            var fm = this.footer.getMargins();
33428             height += (this.footer.getHeight()+fm.top+fm.bottom);
33429         }
33430         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33431         height += this.centerBg.getPadding("tb");
33432         return height;
33433     },
33434
33435     // private
33436     syncBodyHeight : function()
33437     {
33438         var bd = this.body, // the text
33439             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33440             bw = this.bwrap;
33441         var height = this.size.height - this.getHeaderFooterHeight(false);
33442         bd.setHeight(height-bd.getMargins("tb"));
33443         var hh = this.header.getHeight();
33444         var h = this.size.height-hh;
33445         cb.setHeight(h);
33446         
33447         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33448         bw.setHeight(h-cb.getPadding("tb"));
33449         
33450         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33451         bd.setWidth(bw.getWidth(true));
33452         if(this.tabs){
33453             this.tabs.syncHeight();
33454             if(Roo.isIE){
33455                 this.tabs.el.repaint();
33456             }
33457         }
33458     },
33459
33460     /**
33461      * Restores the previous state of the dialog if Roo.state is configured.
33462      * @return {Roo.BasicDialog} this
33463      */
33464     restoreState : function(){
33465         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33466         if(box && box.width){
33467             this.xy = [box.x, box.y];
33468             this.resizeTo(box.width, box.height);
33469         }
33470         return this;
33471     },
33472
33473     // private
33474     beforeShow : function(){
33475         this.expand();
33476         if(this.fixedcenter){
33477             this.xy = this.el.getCenterXY(true);
33478         }
33479         if(this.modal){
33480             Roo.get(document.body).addClass("x-body-masked");
33481             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33482             this.mask.show();
33483         }
33484         this.constrainXY();
33485     },
33486
33487     // private
33488     animShow : function(){
33489         var b = Roo.get(this.animateTarget).getBox();
33490         this.proxy.setSize(b.width, b.height);
33491         this.proxy.setLocation(b.x, b.y);
33492         this.proxy.show();
33493         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33494                     true, .35, this.showEl.createDelegate(this));
33495     },
33496
33497     /**
33498      * Shows the dialog.
33499      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33500      * @return {Roo.BasicDialog} this
33501      */
33502     show : function(animateTarget){
33503         if (this.fireEvent("beforeshow", this) === false){
33504             return;
33505         }
33506         if(this.syncHeightBeforeShow){
33507             this.syncBodyHeight();
33508         }else if(this.firstShow){
33509             this.firstShow = false;
33510             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33511         }
33512         this.animateTarget = animateTarget || this.animateTarget;
33513         if(!this.el.isVisible()){
33514             this.beforeShow();
33515             if(this.animateTarget && Roo.get(this.animateTarget)){
33516                 this.animShow();
33517             }else{
33518                 this.showEl();
33519             }
33520         }
33521         return this;
33522     },
33523
33524     // private
33525     showEl : function(){
33526         this.proxy.hide();
33527         this.el.setXY(this.xy);
33528         this.el.show();
33529         this.adjustAssets(true);
33530         this.toFront();
33531         this.focus();
33532         // IE peekaboo bug - fix found by Dave Fenwick
33533         if(Roo.isIE){
33534             this.el.repaint();
33535         }
33536         this.fireEvent("show", this);
33537     },
33538
33539     /**
33540      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33541      * dialog itself will receive focus.
33542      */
33543     focus : function(){
33544         if(this.defaultButton){
33545             this.defaultButton.focus();
33546         }else{
33547             this.focusEl.focus();
33548         }
33549     },
33550
33551     // private
33552     constrainXY : function(){
33553         if(this.constraintoviewport !== false){
33554             if(!this.viewSize){
33555                 if(this.container){
33556                     var s = this.container.getSize();
33557                     this.viewSize = [s.width, s.height];
33558                 }else{
33559                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33560                 }
33561             }
33562             var s = Roo.get(this.container||document).getScroll();
33563
33564             var x = this.xy[0], y = this.xy[1];
33565             var w = this.size.width, h = this.size.height;
33566             var vw = this.viewSize[0], vh = this.viewSize[1];
33567             // only move it if it needs it
33568             var moved = false;
33569             // first validate right/bottom
33570             if(x + w > vw+s.left){
33571                 x = vw - w;
33572                 moved = true;
33573             }
33574             if(y + h > vh+s.top){
33575                 y = vh - h;
33576                 moved = true;
33577             }
33578             // then make sure top/left isn't negative
33579             if(x < s.left){
33580                 x = s.left;
33581                 moved = true;
33582             }
33583             if(y < s.top){
33584                 y = s.top;
33585                 moved = true;
33586             }
33587             if(moved){
33588                 // cache xy
33589                 this.xy = [x, y];
33590                 if(this.isVisible()){
33591                     this.el.setLocation(x, y);
33592                     this.adjustAssets();
33593                 }
33594             }
33595         }
33596     },
33597
33598     // private
33599     onDrag : function(){
33600         if(!this.proxyDrag){
33601             this.xy = this.el.getXY();
33602             this.adjustAssets();
33603         }
33604     },
33605
33606     // private
33607     adjustAssets : function(doShow){
33608         var x = this.xy[0], y = this.xy[1];
33609         var w = this.size.width, h = this.size.height;
33610         if(doShow === true){
33611             if(this.shadow){
33612                 this.shadow.show(this.el);
33613             }
33614             if(this.shim){
33615                 this.shim.show();
33616             }
33617         }
33618         if(this.shadow && this.shadow.isVisible()){
33619             this.shadow.show(this.el);
33620         }
33621         if(this.shim && this.shim.isVisible()){
33622             this.shim.setBounds(x, y, w, h);
33623         }
33624     },
33625
33626     // private
33627     adjustViewport : function(w, h){
33628         if(!w || !h){
33629             w = Roo.lib.Dom.getViewWidth();
33630             h = Roo.lib.Dom.getViewHeight();
33631         }
33632         // cache the size
33633         this.viewSize = [w, h];
33634         if(this.modal && this.mask.isVisible()){
33635             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33636             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33637         }
33638         if(this.isVisible()){
33639             this.constrainXY();
33640         }
33641     },
33642
33643     /**
33644      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33645      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33646      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33647      */
33648     destroy : function(removeEl){
33649         if(this.isVisible()){
33650             this.animateTarget = null;
33651             this.hide();
33652         }
33653         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33654         if(this.tabs){
33655             this.tabs.destroy(removeEl);
33656         }
33657         Roo.destroy(
33658              this.shim,
33659              this.proxy,
33660              this.resizer,
33661              this.close,
33662              this.mask
33663         );
33664         if(this.dd){
33665             this.dd.unreg();
33666         }
33667         if(this.buttons){
33668            for(var i = 0, len = this.buttons.length; i < len; i++){
33669                this.buttons[i].destroy();
33670            }
33671         }
33672         this.el.removeAllListeners();
33673         if(removeEl === true){
33674             this.el.update("");
33675             this.el.remove();
33676         }
33677         Roo.DialogManager.unregister(this);
33678     },
33679
33680     // private
33681     startMove : function(){
33682         if(this.proxyDrag){
33683             this.proxy.show();
33684         }
33685         if(this.constraintoviewport !== false){
33686             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33687         }
33688     },
33689
33690     // private
33691     endMove : function(){
33692         if(!this.proxyDrag){
33693             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33694         }else{
33695             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33696             this.proxy.hide();
33697         }
33698         this.refreshSize();
33699         this.adjustAssets();
33700         this.focus();
33701         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33702     },
33703
33704     /**
33705      * Brings this dialog to the front of any other visible dialogs
33706      * @return {Roo.BasicDialog} this
33707      */
33708     toFront : function(){
33709         Roo.DialogManager.bringToFront(this);
33710         return this;
33711     },
33712
33713     /**
33714      * Sends this dialog to the back (under) of any other visible dialogs
33715      * @return {Roo.BasicDialog} this
33716      */
33717     toBack : function(){
33718         Roo.DialogManager.sendToBack(this);
33719         return this;
33720     },
33721
33722     /**
33723      * Centers this dialog in the viewport
33724      * @return {Roo.BasicDialog} this
33725      */
33726     center : function(){
33727         var xy = this.el.getCenterXY(true);
33728         this.moveTo(xy[0], xy[1]);
33729         return this;
33730     },
33731
33732     /**
33733      * Moves the dialog's top-left corner to the specified point
33734      * @param {Number} x
33735      * @param {Number} y
33736      * @return {Roo.BasicDialog} this
33737      */
33738     moveTo : function(x, y){
33739         this.xy = [x,y];
33740         if(this.isVisible()){
33741             this.el.setXY(this.xy);
33742             this.adjustAssets();
33743         }
33744         return this;
33745     },
33746
33747     /**
33748      * Aligns the dialog to the specified element
33749      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33750      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33751      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33752      * @return {Roo.BasicDialog} this
33753      */
33754     alignTo : function(element, position, offsets){
33755         this.xy = this.el.getAlignToXY(element, position, offsets);
33756         if(this.isVisible()){
33757             this.el.setXY(this.xy);
33758             this.adjustAssets();
33759         }
33760         return this;
33761     },
33762
33763     /**
33764      * Anchors an element to another element and realigns it when the window is resized.
33765      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33766      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33767      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33768      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33769      * is a number, it is used as the buffer delay (defaults to 50ms).
33770      * @return {Roo.BasicDialog} this
33771      */
33772     anchorTo : function(el, alignment, offsets, monitorScroll){
33773         var action = function(){
33774             this.alignTo(el, alignment, offsets);
33775         };
33776         Roo.EventManager.onWindowResize(action, this);
33777         var tm = typeof monitorScroll;
33778         if(tm != 'undefined'){
33779             Roo.EventManager.on(window, 'scroll', action, this,
33780                 {buffer: tm == 'number' ? monitorScroll : 50});
33781         }
33782         action.call(this);
33783         return this;
33784     },
33785
33786     /**
33787      * Returns true if the dialog is visible
33788      * @return {Boolean}
33789      */
33790     isVisible : function(){
33791         return this.el.isVisible();
33792     },
33793
33794     // private
33795     animHide : function(callback){
33796         var b = Roo.get(this.animateTarget).getBox();
33797         this.proxy.show();
33798         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33799         this.el.hide();
33800         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33801                     this.hideEl.createDelegate(this, [callback]));
33802     },
33803
33804     /**
33805      * Hides the dialog.
33806      * @param {Function} callback (optional) Function to call when the dialog is hidden
33807      * @return {Roo.BasicDialog} this
33808      */
33809     hide : function(callback){
33810         if (this.fireEvent("beforehide", this) === false){
33811             return;
33812         }
33813         if(this.shadow){
33814             this.shadow.hide();
33815         }
33816         if(this.shim) {
33817           this.shim.hide();
33818         }
33819         // sometimes animateTarget seems to get set.. causing problems...
33820         // this just double checks..
33821         if(this.animateTarget && Roo.get(this.animateTarget)) {
33822            this.animHide(callback);
33823         }else{
33824             this.el.hide();
33825             this.hideEl(callback);
33826         }
33827         return this;
33828     },
33829
33830     // private
33831     hideEl : function(callback){
33832         this.proxy.hide();
33833         if(this.modal){
33834             this.mask.hide();
33835             Roo.get(document.body).removeClass("x-body-masked");
33836         }
33837         this.fireEvent("hide", this);
33838         if(typeof callback == "function"){
33839             callback();
33840         }
33841     },
33842
33843     // private
33844     hideAction : function(){
33845         this.setLeft("-10000px");
33846         this.setTop("-10000px");
33847         this.setStyle("visibility", "hidden");
33848     },
33849
33850     // private
33851     refreshSize : function(){
33852         this.size = this.el.getSize();
33853         this.xy = this.el.getXY();
33854         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33855     },
33856
33857     // private
33858     // z-index is managed by the DialogManager and may be overwritten at any time
33859     setZIndex : function(index){
33860         if(this.modal){
33861             this.mask.setStyle("z-index", index);
33862         }
33863         if(this.shim){
33864             this.shim.setStyle("z-index", ++index);
33865         }
33866         if(this.shadow){
33867             this.shadow.setZIndex(++index);
33868         }
33869         this.el.setStyle("z-index", ++index);
33870         if(this.proxy){
33871             this.proxy.setStyle("z-index", ++index);
33872         }
33873         if(this.resizer){
33874             this.resizer.proxy.setStyle("z-index", ++index);
33875         }
33876
33877         this.lastZIndex = index;
33878     },
33879
33880     /**
33881      * Returns the element for this dialog
33882      * @return {Roo.Element} The underlying dialog Element
33883      */
33884     getEl : function(){
33885         return this.el;
33886     }
33887 });
33888
33889 /**
33890  * @class Roo.DialogManager
33891  * Provides global access to BasicDialogs that have been created and
33892  * support for z-indexing (layering) multiple open dialogs.
33893  */
33894 Roo.DialogManager = function(){
33895     var list = {};
33896     var accessList = [];
33897     var front = null;
33898
33899     // private
33900     var sortDialogs = function(d1, d2){
33901         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33902     };
33903
33904     // private
33905     var orderDialogs = function(){
33906         accessList.sort(sortDialogs);
33907         var seed = Roo.DialogManager.zseed;
33908         for(var i = 0, len = accessList.length; i < len; i++){
33909             var dlg = accessList[i];
33910             if(dlg){
33911                 dlg.setZIndex(seed + (i*10));
33912             }
33913         }
33914     };
33915
33916     return {
33917         /**
33918          * The starting z-index for BasicDialogs (defaults to 9000)
33919          * @type Number The z-index value
33920          */
33921         zseed : 9000,
33922
33923         // private
33924         register : function(dlg){
33925             list[dlg.id] = dlg;
33926             accessList.push(dlg);
33927         },
33928
33929         // private
33930         unregister : function(dlg){
33931             delete list[dlg.id];
33932             var i=0;
33933             var len=0;
33934             if(!accessList.indexOf){
33935                 for(  i = 0, len = accessList.length; i < len; i++){
33936                     if(accessList[i] == dlg){
33937                         accessList.splice(i, 1);
33938                         return;
33939                     }
33940                 }
33941             }else{
33942                  i = accessList.indexOf(dlg);
33943                 if(i != -1){
33944                     accessList.splice(i, 1);
33945                 }
33946             }
33947         },
33948
33949         /**
33950          * Gets a registered dialog by id
33951          * @param {String/Object} id The id of the dialog or a dialog
33952          * @return {Roo.BasicDialog} this
33953          */
33954         get : function(id){
33955             return typeof id == "object" ? id : list[id];
33956         },
33957
33958         /**
33959          * Brings the specified dialog to the front
33960          * @param {String/Object} dlg The id of the dialog or a dialog
33961          * @return {Roo.BasicDialog} this
33962          */
33963         bringToFront : function(dlg){
33964             dlg = this.get(dlg);
33965             if(dlg != front){
33966                 front = dlg;
33967                 dlg._lastAccess = new Date().getTime();
33968                 orderDialogs();
33969             }
33970             return dlg;
33971         },
33972
33973         /**
33974          * Sends the specified dialog to the back
33975          * @param {String/Object} dlg The id of the dialog or a dialog
33976          * @return {Roo.BasicDialog} this
33977          */
33978         sendToBack : function(dlg){
33979             dlg = this.get(dlg);
33980             dlg._lastAccess = -(new Date().getTime());
33981             orderDialogs();
33982             return dlg;
33983         },
33984
33985         /**
33986          * Hides all dialogs
33987          */
33988         hideAll : function(){
33989             for(var id in list){
33990                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33991                     list[id].hide();
33992                 }
33993             }
33994         }
33995     };
33996 }();
33997
33998 /**
33999  * @class Roo.LayoutDialog
34000  * @extends Roo.BasicDialog
34001  * @children Roo.ContentPanel
34002  * @parent builder none
34003  * Dialog which provides adjustments for working with a layout in a Dialog.
34004  * Add your necessary layout config options to the dialog's config.<br>
34005  * Example usage (including a nested layout):
34006  * <pre><code>
34007 if(!dialog){
34008     dialog = new Roo.LayoutDialog("download-dlg", {
34009         modal: true,
34010         width:600,
34011         height:450,
34012         shadow:true,
34013         minWidth:500,
34014         minHeight:350,
34015         autoTabs:true,
34016         proxyDrag:true,
34017         // layout config merges with the dialog config
34018         center:{
34019             tabPosition: "top",
34020             alwaysShowTabs: true
34021         }
34022     });
34023     dialog.addKeyListener(27, dialog.hide, dialog);
34024     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34025     dialog.addButton("Build It!", this.getDownload, this);
34026
34027     // we can even add nested layouts
34028     var innerLayout = new Roo.BorderLayout("dl-inner", {
34029         east: {
34030             initialSize: 200,
34031             autoScroll:true,
34032             split:true
34033         },
34034         center: {
34035             autoScroll:true
34036         }
34037     });
34038     innerLayout.beginUpdate();
34039     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34040     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34041     innerLayout.endUpdate(true);
34042
34043     var layout = dialog.getLayout();
34044     layout.beginUpdate();
34045     layout.add("center", new Roo.ContentPanel("standard-panel",
34046                         {title: "Download the Source", fitToFrame:true}));
34047     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34048                {title: "Build your own roo.js"}));
34049     layout.getRegion("center").showPanel(sp);
34050     layout.endUpdate();
34051 }
34052 </code></pre>
34053     * @constructor
34054     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34055     * @param {Object} config configuration options
34056   */
34057 Roo.LayoutDialog = function(el, cfg){
34058     
34059     var config=  cfg;
34060     if (typeof(cfg) == 'undefined') {
34061         config = Roo.apply({}, el);
34062         // not sure why we use documentElement here.. - it should always be body.
34063         // IE7 borks horribly if we use documentElement.
34064         // webkit also does not like documentElement - it creates a body element...
34065         el = Roo.get( document.body || document.documentElement ).createChild();
34066         //config.autoCreate = true;
34067     }
34068     
34069     
34070     config.autoTabs = false;
34071     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34072     this.body.setStyle({overflow:"hidden", position:"relative"});
34073     this.layout = new Roo.BorderLayout(this.body.dom, config);
34074     this.layout.monitorWindowResize = false;
34075     this.el.addClass("x-dlg-auto-layout");
34076     // fix case when center region overwrites center function
34077     this.center = Roo.BasicDialog.prototype.center;
34078     this.on("show", this.layout.layout, this.layout, true);
34079     if (config.items) {
34080         var xitems = config.items;
34081         delete config.items;
34082         Roo.each(xitems, this.addxtype, this);
34083     }
34084     
34085     
34086 };
34087 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34088     
34089     
34090     /**
34091      * @cfg {Roo.LayoutRegion} east  
34092      */
34093     /**
34094      * @cfg {Roo.LayoutRegion} west
34095      */
34096     /**
34097      * @cfg {Roo.LayoutRegion} south
34098      */
34099     /**
34100      * @cfg {Roo.LayoutRegion} north
34101      */
34102     /**
34103      * @cfg {Roo.LayoutRegion} center
34104      */
34105     /**
34106      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34107      */
34108     
34109     
34110     /**
34111      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34112      * @deprecated
34113      */
34114     endUpdate : function(){
34115         this.layout.endUpdate();
34116     },
34117
34118     /**
34119      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34120      *  @deprecated
34121      */
34122     beginUpdate : function(){
34123         this.layout.beginUpdate();
34124     },
34125
34126     /**
34127      * Get the BorderLayout for this dialog
34128      * @return {Roo.BorderLayout}
34129      */
34130     getLayout : function(){
34131         return this.layout;
34132     },
34133
34134     showEl : function(){
34135         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34136         if(Roo.isIE7){
34137             this.layout.layout();
34138         }
34139     },
34140
34141     // private
34142     // Use the syncHeightBeforeShow config option to control this automatically
34143     syncBodyHeight : function(){
34144         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34145         if(this.layout){this.layout.layout();}
34146     },
34147     
34148       /**
34149      * Add an xtype element (actually adds to the layout.)
34150      * @return {Object} xdata xtype object data.
34151      */
34152     
34153     addxtype : function(c) {
34154         return this.layout.addxtype(c);
34155     }
34156 });/*
34157  * Based on:
34158  * Ext JS Library 1.1.1
34159  * Copyright(c) 2006-2007, Ext JS, LLC.
34160  *
34161  * Originally Released Under LGPL - original licence link has changed is not relivant.
34162  *
34163  * Fork - LGPL
34164  * <script type="text/javascript">
34165  */
34166  
34167 /**
34168  * @class Roo.MessageBox
34169  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34170  * Example usage:
34171  *<pre><code>
34172 // Basic alert:
34173 Roo.Msg.alert('Status', 'Changes saved successfully.');
34174
34175 // Prompt for user data:
34176 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34177     if (btn == 'ok'){
34178         // process text value...
34179     }
34180 });
34181
34182 // Show a dialog using config options:
34183 Roo.Msg.show({
34184    title:'Save Changes?',
34185    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34186    buttons: Roo.Msg.YESNOCANCEL,
34187    fn: processResult,
34188    animEl: 'elId'
34189 });
34190 </code></pre>
34191  * @static
34192  */
34193 Roo.MessageBox = function(){
34194     var dlg, opt, mask, waitTimer;
34195     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34196     var buttons, activeTextEl, bwidth;
34197
34198     // private
34199     var handleButton = function(button){
34200         dlg.hide();
34201         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34202     };
34203
34204     // private
34205     var handleHide = function(){
34206         if(opt && opt.cls){
34207             dlg.el.removeClass(opt.cls);
34208         }
34209         if(waitTimer){
34210             Roo.TaskMgr.stop(waitTimer);
34211             waitTimer = null;
34212         }
34213     };
34214
34215     // private
34216     var updateButtons = function(b){
34217         var width = 0;
34218         if(!b){
34219             buttons["ok"].hide();
34220             buttons["cancel"].hide();
34221             buttons["yes"].hide();
34222             buttons["no"].hide();
34223             dlg.footer.dom.style.display = 'none';
34224             return width;
34225         }
34226         dlg.footer.dom.style.display = '';
34227         for(var k in buttons){
34228             if(typeof buttons[k] != "function"){
34229                 if(b[k]){
34230                     buttons[k].show();
34231                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34232                     width += buttons[k].el.getWidth()+15;
34233                 }else{
34234                     buttons[k].hide();
34235                 }
34236             }
34237         }
34238         return width;
34239     };
34240
34241     // private
34242     var handleEsc = function(d, k, e){
34243         if(opt && opt.closable !== false){
34244             dlg.hide();
34245         }
34246         if(e){
34247             e.stopEvent();
34248         }
34249     };
34250
34251     return {
34252         /**
34253          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34254          * @return {Roo.BasicDialog} The BasicDialog element
34255          */
34256         getDialog : function(){
34257            if(!dlg){
34258                 dlg = new Roo.BasicDialog("x-msg-box", {
34259                     autoCreate : true,
34260                     shadow: true,
34261                     draggable: true,
34262                     resizable:false,
34263                     constraintoviewport:false,
34264                     fixedcenter:true,
34265                     collapsible : false,
34266                     shim:true,
34267                     modal: true,
34268                     width:400, height:100,
34269                     buttonAlign:"center",
34270                     closeClick : function(){
34271                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34272                             handleButton("no");
34273                         }else{
34274                             handleButton("cancel");
34275                         }
34276                     }
34277                 });
34278                 dlg.on("hide", handleHide);
34279                 mask = dlg.mask;
34280                 dlg.addKeyListener(27, handleEsc);
34281                 buttons = {};
34282                 var bt = this.buttonText;
34283                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34284                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34285                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34286                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34287                 bodyEl = dlg.body.createChild({
34288
34289                     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>'
34290                 });
34291                 msgEl = bodyEl.dom.firstChild;
34292                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34293                 textboxEl.enableDisplayMode();
34294                 textboxEl.addKeyListener([10,13], function(){
34295                     if(dlg.isVisible() && opt && opt.buttons){
34296                         if(opt.buttons.ok){
34297                             handleButton("ok");
34298                         }else if(opt.buttons.yes){
34299                             handleButton("yes");
34300                         }
34301                     }
34302                 });
34303                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34304                 textareaEl.enableDisplayMode();
34305                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34306                 progressEl.enableDisplayMode();
34307                 var pf = progressEl.dom.firstChild;
34308                 if (pf) {
34309                     pp = Roo.get(pf.firstChild);
34310                     pp.setHeight(pf.offsetHeight);
34311                 }
34312                 
34313             }
34314             return dlg;
34315         },
34316
34317         /**
34318          * Updates the message box body text
34319          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34320          * the XHTML-compliant non-breaking space character '&amp;#160;')
34321          * @return {Roo.MessageBox} This message box
34322          */
34323         updateText : function(text){
34324             if(!dlg.isVisible() && !opt.width){
34325                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34326             }
34327             msgEl.innerHTML = text || '&#160;';
34328       
34329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34331             var w = Math.max(
34332                     Math.min(opt.width || cw , this.maxWidth), 
34333                     Math.max(opt.minWidth || this.minWidth, bwidth)
34334             );
34335             if(opt.prompt){
34336                 activeTextEl.setWidth(w);
34337             }
34338             if(dlg.isVisible()){
34339                 dlg.fixedcenter = false;
34340             }
34341             // to big, make it scroll. = But as usual stupid IE does not support
34342             // !important..
34343             
34344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34347             } else {
34348                 bodyEl.dom.style.height = '';
34349                 bodyEl.dom.style.overflowY = '';
34350             }
34351             if (cw > w) {
34352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34353             } else {
34354                 bodyEl.dom.style.overflowX = '';
34355             }
34356             
34357             dlg.setContentSize(w, bodyEl.getHeight());
34358             if(dlg.isVisible()){
34359                 dlg.fixedcenter = true;
34360             }
34361             return this;
34362         },
34363
34364         /**
34365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34369          * @return {Roo.MessageBox} This message box
34370          */
34371         updateProgress : function(value, text){
34372             if(text){
34373                 this.updateText(text);
34374             }
34375             if (pp) { // weird bug on my firefox - for some reason this is not defined
34376                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34377             }
34378             return this;
34379         },        
34380
34381         /**
34382          * Returns true if the message box is currently displayed
34383          * @return {Boolean} True if the message box is visible, else false
34384          */
34385         isVisible : function(){
34386             return dlg && dlg.isVisible();  
34387         },
34388
34389         /**
34390          * Hides the message box if it is displayed
34391          */
34392         hide : function(){
34393             if(this.isVisible()){
34394                 dlg.hide();
34395             }  
34396         },
34397
34398         /**
34399          * Displays a new message box, or reinitializes an existing message box, based on the config options
34400          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34401          * The following config object properties are supported:
34402          * <pre>
34403 Property    Type             Description
34404 ----------  ---------------  ------------------------------------------------------------------------------------
34405 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34406                                    closes (defaults to undefined)
34407 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34408                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34409 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34410                                    progress and wait dialogs will ignore this property and always hide the
34411                                    close button as they can only be closed programmatically.
34412 cls               String           A custom CSS class to apply to the message box element
34413 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34414                                    displayed (defaults to 75)
34415 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34416                                    function will be btn (the name of the button that was clicked, if applicable,
34417                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34418                                    Progress and wait dialogs will ignore this option since they do not respond to
34419                                    user actions and can only be closed programmatically, so any required function
34420                                    should be called by the same code after it closes the dialog.
34421 icon              String           A CSS class that provides a background image to be used as an icon for
34422                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34423 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34424 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34425 modal             Boolean          False to allow user interaction with the page while the message box is
34426                                    displayed (defaults to true)
34427 msg               String           A string that will replace the existing message box body text (defaults
34428                                    to the XHTML-compliant non-breaking space character '&#160;')
34429 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34430 progress          Boolean          True to display a progress bar (defaults to false)
34431 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34432 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34433 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34434 title             String           The title text
34435 value             String           The string value to set into the active textbox element if displayed
34436 wait              Boolean          True to display a progress bar (defaults to false)
34437 width             Number           The width of the dialog in pixels
34438 </pre>
34439          *
34440          * Example usage:
34441          * <pre><code>
34442 Roo.Msg.show({
34443    title: 'Address',
34444    msg: 'Please enter your address:',
34445    width: 300,
34446    buttons: Roo.MessageBox.OKCANCEL,
34447    multiline: true,
34448    fn: saveAddress,
34449    animEl: 'addAddressBtn'
34450 });
34451 </code></pre>
34452          * @param {Object} config Configuration options
34453          * @return {Roo.MessageBox} This message box
34454          */
34455         show : function(options)
34456         {
34457             
34458             // this causes nightmares if you show one dialog after another
34459             // especially on callbacks..
34460              
34461             if(this.isVisible()){
34462                 
34463                 this.hide();
34464                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34465                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34466                 Roo.log("New Dialog Message:" +  options.msg )
34467                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34468                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34469                 
34470             }
34471             var d = this.getDialog();
34472             opt = options;
34473             d.setTitle(opt.title || "&#160;");
34474             d.close.setDisplayed(opt.closable !== false);
34475             activeTextEl = textboxEl;
34476             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34477             if(opt.prompt){
34478                 if(opt.multiline){
34479                     textboxEl.hide();
34480                     textareaEl.show();
34481                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34482                         opt.multiline : this.defaultTextHeight);
34483                     activeTextEl = textareaEl;
34484                 }else{
34485                     textboxEl.show();
34486                     textareaEl.hide();
34487                 }
34488             }else{
34489                 textboxEl.hide();
34490                 textareaEl.hide();
34491             }
34492             progressEl.setDisplayed(opt.progress === true);
34493             this.updateProgress(0);
34494             activeTextEl.dom.value = opt.value || "";
34495             if(opt.prompt){
34496                 dlg.setDefaultButton(activeTextEl);
34497             }else{
34498                 var bs = opt.buttons;
34499                 var db = null;
34500                 if(bs && bs.ok){
34501                     db = buttons["ok"];
34502                 }else if(bs && bs.yes){
34503                     db = buttons["yes"];
34504                 }
34505                 dlg.setDefaultButton(db);
34506             }
34507             bwidth = updateButtons(opt.buttons);
34508             this.updateText(opt.msg);
34509             if(opt.cls){
34510                 d.el.addClass(opt.cls);
34511             }
34512             d.proxyDrag = opt.proxyDrag === true;
34513             d.modal = opt.modal !== false;
34514             d.mask = opt.modal !== false ? mask : false;
34515             if(!d.isVisible()){
34516                 // force it to the end of the z-index stack so it gets a cursor in FF
34517                 document.body.appendChild(dlg.el.dom);
34518                 d.animateTarget = null;
34519                 d.show(options.animEl);
34520             }
34521             return this;
34522         },
34523
34524         /**
34525          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34526          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34527          * and closing the message box when the process is complete.
34528          * @param {String} title The title bar text
34529          * @param {String} msg The message box body text
34530          * @return {Roo.MessageBox} This message box
34531          */
34532         progress : function(title, msg){
34533             this.show({
34534                 title : title,
34535                 msg : msg,
34536                 buttons: false,
34537                 progress:true,
34538                 closable:false,
34539                 minWidth: this.minProgressWidth,
34540                 modal : true
34541             });
34542             return this;
34543         },
34544
34545         /**
34546          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34547          * If a callback function is passed it will be called after the user clicks the button, and the
34548          * id of the button that was clicked will be passed as the only parameter to the callback
34549          * (could also be the top-right close button).
34550          * @param {String} title The title bar text
34551          * @param {String} msg The message box body text
34552          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34553          * @param {Object} scope (optional) The scope of the callback function
34554          * @return {Roo.MessageBox} This message box
34555          */
34556         alert : function(title, msg, fn, scope){
34557             this.show({
34558                 title : title,
34559                 msg : msg,
34560                 buttons: this.OK,
34561                 fn: fn,
34562                 scope : scope,
34563                 modal : true
34564             });
34565             return this;
34566         },
34567
34568         /**
34569          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34570          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34571          * You are responsible for closing the message box when the process is complete.
34572          * @param {String} msg The message box body text
34573          * @param {String} title (optional) The title bar text
34574          * @return {Roo.MessageBox} This message box
34575          */
34576         wait : function(msg, title){
34577             this.show({
34578                 title : title,
34579                 msg : msg,
34580                 buttons: false,
34581                 closable:false,
34582                 progress:true,
34583                 modal:true,
34584                 width:300,
34585                 wait:true
34586             });
34587             waitTimer = Roo.TaskMgr.start({
34588                 run: function(i){
34589                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34590                 },
34591                 interval: 1000
34592             });
34593             return this;
34594         },
34595
34596         /**
34597          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34598          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34599          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34600          * @param {String} title The title bar text
34601          * @param {String} msg The message box body text
34602          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34603          * @param {Object} scope (optional) The scope of the callback function
34604          * @return {Roo.MessageBox} This message box
34605          */
34606         confirm : function(title, msg, fn, scope){
34607             this.show({
34608                 title : title,
34609                 msg : msg,
34610                 buttons: this.YESNO,
34611                 fn: fn,
34612                 scope : scope,
34613                 modal : true
34614             });
34615             return this;
34616         },
34617
34618         /**
34619          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34620          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34621          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34622          * (could also be the top-right close button) and the text that was entered will be passed as the two
34623          * parameters to the callback.
34624          * @param {String} title The title bar text
34625          * @param {String} msg The message box body text
34626          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34627          * @param {Object} scope (optional) The scope of the callback function
34628          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34629          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34630          * @return {Roo.MessageBox} This message box
34631          */
34632         prompt : function(title, msg, fn, scope, multiline){
34633             this.show({
34634                 title : title,
34635                 msg : msg,
34636                 buttons: this.OKCANCEL,
34637                 fn: fn,
34638                 minWidth:250,
34639                 scope : scope,
34640                 prompt:true,
34641                 multiline: multiline,
34642                 modal : true
34643             });
34644             return this;
34645         },
34646
34647         /**
34648          * Button config that displays a single OK button
34649          * @type Object
34650          */
34651         OK : {ok:true},
34652         /**
34653          * Button config that displays Yes and No buttons
34654          * @type Object
34655          */
34656         YESNO : {yes:true, no:true},
34657         /**
34658          * Button config that displays OK and Cancel buttons
34659          * @type Object
34660          */
34661         OKCANCEL : {ok:true, cancel:true},
34662         /**
34663          * Button config that displays Yes, No and Cancel buttons
34664          * @type Object
34665          */
34666         YESNOCANCEL : {yes:true, no:true, cancel:true},
34667
34668         /**
34669          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34670          * @type Number
34671          */
34672         defaultTextHeight : 75,
34673         /**
34674          * The maximum width in pixels of the message box (defaults to 600)
34675          * @type Number
34676          */
34677         maxWidth : 600,
34678         /**
34679          * The minimum width in pixels of the message box (defaults to 100)
34680          * @type Number
34681          */
34682         minWidth : 100,
34683         /**
34684          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34685          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34686          * @type Number
34687          */
34688         minProgressWidth : 250,
34689         /**
34690          * An object containing the default button text strings that can be overriden for localized language support.
34691          * Supported properties are: ok, cancel, yes and no.
34692          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34693          * @type Object
34694          */
34695         buttonText : {
34696             ok : "OK",
34697             cancel : "Cancel",
34698             yes : "Yes",
34699             no : "No"
34700         }
34701     };
34702 }();
34703
34704 /**
34705  * Shorthand for {@link Roo.MessageBox}
34706  */
34707 Roo.Msg = Roo.MessageBox;/*
34708  * Based on:
34709  * Ext JS Library 1.1.1
34710  * Copyright(c) 2006-2007, Ext JS, LLC.
34711  *
34712  * Originally Released Under LGPL - original licence link has changed is not relivant.
34713  *
34714  * Fork - LGPL
34715  * <script type="text/javascript">
34716  */
34717 /**
34718  * @class Roo.QuickTips
34719  * Provides attractive and customizable tooltips for any element.
34720  * @static
34721  */
34722 Roo.QuickTips = function(){
34723     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34724     var ce, bd, xy, dd;
34725     var visible = false, disabled = true, inited = false;
34726     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34727     
34728     var onOver = function(e){
34729         if(disabled){
34730             return;
34731         }
34732         var t = e.getTarget();
34733         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34734             return;
34735         }
34736         if(ce && t == ce.el){
34737             clearTimeout(hideProc);
34738             return;
34739         }
34740         if(t && tagEls[t.id]){
34741             tagEls[t.id].el = t;
34742             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34743             return;
34744         }
34745         var ttp, et = Roo.fly(t);
34746         var ns = cfg.namespace;
34747         if(tm.interceptTitles && t.title){
34748             ttp = t.title;
34749             t.qtip = ttp;
34750             t.removeAttribute("title");
34751             e.preventDefault();
34752         }else{
34753             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34754         }
34755         if(ttp){
34756             showProc = show.defer(tm.showDelay, tm, [{
34757                 el: t, 
34758                 text: ttp.replace(/\\n/g,'<br/>'),
34759                 width: et.getAttributeNS(ns, cfg.width),
34760                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34761                 title: et.getAttributeNS(ns, cfg.title),
34762                     cls: et.getAttributeNS(ns, cfg.cls)
34763             }]);
34764         }
34765     };
34766     
34767     var onOut = function(e){
34768         clearTimeout(showProc);
34769         var t = e.getTarget();
34770         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34771             hideProc = setTimeout(hide, tm.hideDelay);
34772         }
34773     };
34774     
34775     var onMove = function(e){
34776         if(disabled){
34777             return;
34778         }
34779         xy = e.getXY();
34780         xy[1] += 18;
34781         if(tm.trackMouse && ce){
34782             el.setXY(xy);
34783         }
34784     };
34785     
34786     var onDown = function(e){
34787         clearTimeout(showProc);
34788         clearTimeout(hideProc);
34789         if(!e.within(el)){
34790             if(tm.hideOnClick){
34791                 hide();
34792                 tm.disable();
34793                 tm.enable.defer(100, tm);
34794             }
34795         }
34796     };
34797     
34798     var getPad = function(){
34799         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34800     };
34801
34802     var show = function(o){
34803         if(disabled){
34804             return;
34805         }
34806         clearTimeout(dismissProc);
34807         ce = o;
34808         if(removeCls){ // in case manually hidden
34809             el.removeClass(removeCls);
34810             removeCls = null;
34811         }
34812         if(ce.cls){
34813             el.addClass(ce.cls);
34814             removeCls = ce.cls;
34815         }
34816         if(ce.title){
34817             tipTitle.update(ce.title);
34818             tipTitle.show();
34819         }else{
34820             tipTitle.update('');
34821             tipTitle.hide();
34822         }
34823         el.dom.style.width  = tm.maxWidth+'px';
34824         //tipBody.dom.style.width = '';
34825         tipBodyText.update(o.text);
34826         var p = getPad(), w = ce.width;
34827         if(!w){
34828             var td = tipBodyText.dom;
34829             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34830             if(aw > tm.maxWidth){
34831                 w = tm.maxWidth;
34832             }else if(aw < tm.minWidth){
34833                 w = tm.minWidth;
34834             }else{
34835                 w = aw;
34836             }
34837         }
34838         //tipBody.setWidth(w);
34839         el.setWidth(parseInt(w, 10) + p);
34840         if(ce.autoHide === false){
34841             close.setDisplayed(true);
34842             if(dd){
34843                 dd.unlock();
34844             }
34845         }else{
34846             close.setDisplayed(false);
34847             if(dd){
34848                 dd.lock();
34849             }
34850         }
34851         if(xy){
34852             el.avoidY = xy[1]-18;
34853             el.setXY(xy);
34854         }
34855         if(tm.animate){
34856             el.setOpacity(.1);
34857             el.setStyle("visibility", "visible");
34858             el.fadeIn({callback: afterShow});
34859         }else{
34860             afterShow();
34861         }
34862     };
34863     
34864     var afterShow = function(){
34865         if(ce){
34866             el.show();
34867             esc.enable();
34868             if(tm.autoDismiss && ce.autoHide !== false){
34869                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34870             }
34871         }
34872     };
34873     
34874     var hide = function(noanim){
34875         clearTimeout(dismissProc);
34876         clearTimeout(hideProc);
34877         ce = null;
34878         if(el.isVisible()){
34879             esc.disable();
34880             if(noanim !== true && tm.animate){
34881                 el.fadeOut({callback: afterHide});
34882             }else{
34883                 afterHide();
34884             } 
34885         }
34886     };
34887     
34888     var afterHide = function(){
34889         el.hide();
34890         if(removeCls){
34891             el.removeClass(removeCls);
34892             removeCls = null;
34893         }
34894     };
34895     
34896     return {
34897         /**
34898         * @cfg {Number} minWidth
34899         * The minimum width of the quick tip (defaults to 40)
34900         */
34901        minWidth : 40,
34902         /**
34903         * @cfg {Number} maxWidth
34904         * The maximum width of the quick tip (defaults to 300)
34905         */
34906        maxWidth : 300,
34907         /**
34908         * @cfg {Boolean} interceptTitles
34909         * True to automatically use the element's DOM title value if available (defaults to false)
34910         */
34911        interceptTitles : false,
34912         /**
34913         * @cfg {Boolean} trackMouse
34914         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34915         */
34916        trackMouse : false,
34917         /**
34918         * @cfg {Boolean} hideOnClick
34919         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34920         */
34921        hideOnClick : true,
34922         /**
34923         * @cfg {Number} showDelay
34924         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34925         */
34926        showDelay : 500,
34927         /**
34928         * @cfg {Number} hideDelay
34929         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34930         */
34931        hideDelay : 200,
34932         /**
34933         * @cfg {Boolean} autoHide
34934         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34935         * Used in conjunction with hideDelay.
34936         */
34937        autoHide : true,
34938         /**
34939         * @cfg {Boolean}
34940         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34941         * (defaults to true).  Used in conjunction with autoDismissDelay.
34942         */
34943        autoDismiss : true,
34944         /**
34945         * @cfg {Number}
34946         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34947         */
34948        autoDismissDelay : 5000,
34949        /**
34950         * @cfg {Boolean} animate
34951         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34952         */
34953        animate : false,
34954
34955        /**
34956         * @cfg {String} title
34957         * Title text to display (defaults to '').  This can be any valid HTML markup.
34958         */
34959         title: '',
34960        /**
34961         * @cfg {String} text
34962         * Body text to display (defaults to '').  This can be any valid HTML markup.
34963         */
34964         text : '',
34965        /**
34966         * @cfg {String} cls
34967         * A CSS class to apply to the base quick tip element (defaults to '').
34968         */
34969         cls : '',
34970        /**
34971         * @cfg {Number} width
34972         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34973         * minWidth or maxWidth.
34974         */
34975         width : null,
34976
34977     /**
34978      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34979      * or display QuickTips in a page.
34980      */
34981        init : function(){
34982           tm = Roo.QuickTips;
34983           cfg = tm.tagConfig;
34984           if(!inited){
34985               if(!Roo.isReady){ // allow calling of init() before onReady
34986                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34987                   return;
34988               }
34989               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34990               el.fxDefaults = {stopFx: true};
34991               // maximum custom styling
34992               //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>');
34993               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>');              
34994               tipTitle = el.child('h3');
34995               tipTitle.enableDisplayMode("block");
34996               tipBody = el.child('div.x-tip-bd');
34997               tipBodyText = el.child('div.x-tip-bd-inner');
34998               //bdLeft = el.child('div.x-tip-bd-left');
34999               //bdRight = el.child('div.x-tip-bd-right');
35000               close = el.child('div.x-tip-close');
35001               close.enableDisplayMode("block");
35002               close.on("click", hide);
35003               var d = Roo.get(document);
35004               d.on("mousedown", onDown);
35005               d.on("mouseover", onOver);
35006               d.on("mouseout", onOut);
35007               d.on("mousemove", onMove);
35008               esc = d.addKeyListener(27, hide);
35009               esc.disable();
35010               if(Roo.dd.DD){
35011                   dd = el.initDD("default", null, {
35012                       onDrag : function(){
35013                           el.sync();  
35014                       }
35015                   });
35016                   dd.setHandleElId(tipTitle.id);
35017                   dd.lock();
35018               }
35019               inited = true;
35020           }
35021           this.enable(); 
35022        },
35023
35024     /**
35025      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35026      * are supported:
35027      * <pre>
35028 Property    Type                   Description
35029 ----------  ---------------------  ------------------------------------------------------------------------
35030 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35031      * </ul>
35032      * @param {Object} config The config object
35033      */
35034        register : function(config){
35035            var cs = config instanceof Array ? config : arguments;
35036            for(var i = 0, len = cs.length; i < len; i++) {
35037                var c = cs[i];
35038                var target = c.target;
35039                if(target){
35040                    if(target instanceof Array){
35041                        for(var j = 0, jlen = target.length; j < jlen; j++){
35042                            tagEls[target[j]] = c;
35043                        }
35044                    }else{
35045                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35046                    }
35047                }
35048            }
35049        },
35050
35051     /**
35052      * Removes this quick tip from its element and destroys it.
35053      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35054      */
35055        unregister : function(el){
35056            delete tagEls[Roo.id(el)];
35057        },
35058
35059     /**
35060      * Enable this quick tip.
35061      */
35062        enable : function(){
35063            if(inited && disabled){
35064                locks.pop();
35065                if(locks.length < 1){
35066                    disabled = false;
35067                }
35068            }
35069        },
35070
35071     /**
35072      * Disable this quick tip.
35073      */
35074        disable : function(){
35075           disabled = true;
35076           clearTimeout(showProc);
35077           clearTimeout(hideProc);
35078           clearTimeout(dismissProc);
35079           if(ce){
35080               hide(true);
35081           }
35082           locks.push(1);
35083        },
35084
35085     /**
35086      * Returns true if the quick tip is enabled, else false.
35087      */
35088        isEnabled : function(){
35089             return !disabled;
35090        },
35091
35092         // private
35093        tagConfig : {
35094            namespace : "roo", // was ext?? this may break..
35095            alt_namespace : "ext",
35096            attribute : "qtip",
35097            width : "width",
35098            target : "target",
35099            title : "qtitle",
35100            hide : "hide",
35101            cls : "qclass"
35102        }
35103    };
35104 }();
35105
35106 // backwards compat
35107 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35108  * Based on:
35109  * Ext JS Library 1.1.1
35110  * Copyright(c) 2006-2007, Ext JS, LLC.
35111  *
35112  * Originally Released Under LGPL - original licence link has changed is not relivant.
35113  *
35114  * Fork - LGPL
35115  * <script type="text/javascript">
35116  */
35117  
35118
35119 /**
35120  * @class Roo.tree.TreePanel
35121  * @extends Roo.data.Tree
35122  * @cfg {Roo.tree.TreeNode} root The root node
35123  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35124  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35125  * @cfg {Boolean} enableDD true to enable drag and drop
35126  * @cfg {Boolean} enableDrag true to enable just drag
35127  * @cfg {Boolean} enableDrop true to enable just drop
35128  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35129  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35130  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35131  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35132  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35133  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35134  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35135  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35136  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35137  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35138  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35139  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35140  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35141  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35142  * @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>
35143  * @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>
35144  * 
35145  * @constructor
35146  * @param {String/HTMLElement/Element} el The container element
35147  * @param {Object} config
35148  */
35149 Roo.tree.TreePanel = function(el, config){
35150     var root = false;
35151     var loader = false;
35152     if (config.root) {
35153         root = config.root;
35154         delete config.root;
35155     }
35156     if (config.loader) {
35157         loader = config.loader;
35158         delete config.loader;
35159     }
35160     
35161     Roo.apply(this, config);
35162     Roo.tree.TreePanel.superclass.constructor.call(this);
35163     this.el = Roo.get(el);
35164     this.el.addClass('x-tree');
35165     //console.log(root);
35166     if (root) {
35167         this.setRootNode( Roo.factory(root, Roo.tree));
35168     }
35169     if (loader) {
35170         this.loader = Roo.factory(loader, Roo.tree);
35171     }
35172    /**
35173     * Read-only. The id of the container element becomes this TreePanel's id.
35174     */
35175     this.id = this.el.id;
35176     this.addEvents({
35177         /**
35178         * @event beforeload
35179         * Fires before a node is loaded, return false to cancel
35180         * @param {Node} node The node being loaded
35181         */
35182         "beforeload" : true,
35183         /**
35184         * @event load
35185         * Fires when a node is loaded
35186         * @param {Node} node The node that was loaded
35187         */
35188         "load" : true,
35189         /**
35190         * @event textchange
35191         * Fires when the text for a node is changed
35192         * @param {Node} node The node
35193         * @param {String} text The new text
35194         * @param {String} oldText The old text
35195         */
35196         "textchange" : true,
35197         /**
35198         * @event beforeexpand
35199         * Fires before a node is expanded, return false to cancel.
35200         * @param {Node} node The node
35201         * @param {Boolean} deep
35202         * @param {Boolean} anim
35203         */
35204         "beforeexpand" : true,
35205         /**
35206         * @event beforecollapse
35207         * Fires before a node is collapsed, return false to cancel.
35208         * @param {Node} node The node
35209         * @param {Boolean} deep
35210         * @param {Boolean} anim
35211         */
35212         "beforecollapse" : true,
35213         /**
35214         * @event expand
35215         * Fires when a node is expanded
35216         * @param {Node} node The node
35217         */
35218         "expand" : true,
35219         /**
35220         * @event disabledchange
35221         * Fires when the disabled status of a node changes
35222         * @param {Node} node The node
35223         * @param {Boolean} disabled
35224         */
35225         "disabledchange" : true,
35226         /**
35227         * @event collapse
35228         * Fires when a node is collapsed
35229         * @param {Node} node The node
35230         */
35231         "collapse" : true,
35232         /**
35233         * @event beforeclick
35234         * Fires before click processing on a node. Return false to cancel the default action.
35235         * @param {Node} node The node
35236         * @param {Roo.EventObject} e The event object
35237         */
35238         "beforeclick":true,
35239         /**
35240         * @event checkchange
35241         * Fires when a node with a checkbox's checked property changes
35242         * @param {Node} this This node
35243         * @param {Boolean} checked
35244         */
35245         "checkchange":true,
35246         /**
35247         * @event click
35248         * Fires when a node is clicked
35249         * @param {Node} node The node
35250         * @param {Roo.EventObject} e The event object
35251         */
35252         "click":true,
35253         /**
35254         * @event dblclick
35255         * Fires when a node is double clicked
35256         * @param {Node} node The node
35257         * @param {Roo.EventObject} e The event object
35258         */
35259         "dblclick":true,
35260         /**
35261         * @event contextmenu
35262         * Fires when a node is right clicked
35263         * @param {Node} node The node
35264         * @param {Roo.EventObject} e The event object
35265         */
35266         "contextmenu":true,
35267         /**
35268         * @event beforechildrenrendered
35269         * Fires right before the child nodes for a node are rendered
35270         * @param {Node} node The node
35271         */
35272         "beforechildrenrendered":true,
35273         /**
35274         * @event startdrag
35275         * Fires when a node starts being dragged
35276         * @param {Roo.tree.TreePanel} this
35277         * @param {Roo.tree.TreeNode} node
35278         * @param {event} e The raw browser event
35279         */ 
35280        "startdrag" : true,
35281        /**
35282         * @event enddrag
35283         * Fires when a drag operation is complete
35284         * @param {Roo.tree.TreePanel} this
35285         * @param {Roo.tree.TreeNode} node
35286         * @param {event} e The raw browser event
35287         */
35288        "enddrag" : true,
35289        /**
35290         * @event dragdrop
35291         * Fires when a dragged node is dropped on a valid DD target
35292         * @param {Roo.tree.TreePanel} this
35293         * @param {Roo.tree.TreeNode} node
35294         * @param {DD} dd The dd it was dropped on
35295         * @param {event} e The raw browser event
35296         */
35297        "dragdrop" : true,
35298        /**
35299         * @event beforenodedrop
35300         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35301         * passed to handlers has the following properties:<br />
35302         * <ul style="padding:5px;padding-left:16px;">
35303         * <li>tree - The TreePanel</li>
35304         * <li>target - The node being targeted for the drop</li>
35305         * <li>data - The drag data from the drag source</li>
35306         * <li>point - The point of the drop - append, above or below</li>
35307         * <li>source - The drag source</li>
35308         * <li>rawEvent - Raw mouse event</li>
35309         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35310         * to be inserted by setting them on this object.</li>
35311         * <li>cancel - Set this to true to cancel the drop.</li>
35312         * </ul>
35313         * @param {Object} dropEvent
35314         */
35315        "beforenodedrop" : true,
35316        /**
35317         * @event nodedrop
35318         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35319         * passed to handlers has the following properties:<br />
35320         * <ul style="padding:5px;padding-left:16px;">
35321         * <li>tree - The TreePanel</li>
35322         * <li>target - The node being targeted for the drop</li>
35323         * <li>data - The drag data from the drag source</li>
35324         * <li>point - The point of the drop - append, above or below</li>
35325         * <li>source - The drag source</li>
35326         * <li>rawEvent - Raw mouse event</li>
35327         * <li>dropNode - Dropped node(s).</li>
35328         * </ul>
35329         * @param {Object} dropEvent
35330         */
35331        "nodedrop" : true,
35332         /**
35333         * @event nodedragover
35334         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35335         * passed to handlers has the following properties:<br />
35336         * <ul style="padding:5px;padding-left:16px;">
35337         * <li>tree - The TreePanel</li>
35338         * <li>target - The node being targeted for the drop</li>
35339         * <li>data - The drag data from the drag source</li>
35340         * <li>point - The point of the drop - append, above or below</li>
35341         * <li>source - The drag source</li>
35342         * <li>rawEvent - Raw mouse event</li>
35343         * <li>dropNode - Drop node(s) provided by the source.</li>
35344         * <li>cancel - Set this to true to signal drop not allowed.</li>
35345         * </ul>
35346         * @param {Object} dragOverEvent
35347         */
35348        "nodedragover" : true,
35349        /**
35350         * @event appendnode
35351         * Fires when append node to the tree
35352         * @param {Roo.tree.TreePanel} this
35353         * @param {Roo.tree.TreeNode} node
35354         * @param {Number} index The index of the newly appended node
35355         */
35356        "appendnode" : true
35357         
35358     });
35359     if(this.singleExpand){
35360        this.on("beforeexpand", this.restrictExpand, this);
35361     }
35362     if (this.editor) {
35363         this.editor.tree = this;
35364         this.editor = Roo.factory(this.editor, Roo.tree);
35365     }
35366     
35367     if (this.selModel) {
35368         this.selModel = Roo.factory(this.selModel, Roo.tree);
35369     }
35370    
35371 };
35372 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35373     rootVisible : true,
35374     animate: Roo.enableFx,
35375     lines : true,
35376     enableDD : false,
35377     hlDrop : Roo.enableFx,
35378   
35379     renderer: false,
35380     
35381     rendererTip: false,
35382     // private
35383     restrictExpand : function(node){
35384         var p = node.parentNode;
35385         if(p){
35386             if(p.expandedChild && p.expandedChild.parentNode == p){
35387                 p.expandedChild.collapse();
35388             }
35389             p.expandedChild = node;
35390         }
35391     },
35392
35393     // private override
35394     setRootNode : function(node){
35395         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35396         if(!this.rootVisible){
35397             node.ui = new Roo.tree.RootTreeNodeUI(node);
35398         }
35399         return node;
35400     },
35401
35402     /**
35403      * Returns the container element for this TreePanel
35404      */
35405     getEl : function(){
35406         return this.el;
35407     },
35408
35409     /**
35410      * Returns the default TreeLoader for this TreePanel
35411      */
35412     getLoader : function(){
35413         return this.loader;
35414     },
35415
35416     /**
35417      * Expand all nodes
35418      */
35419     expandAll : function(){
35420         this.root.expand(true);
35421     },
35422
35423     /**
35424      * Collapse all nodes
35425      */
35426     collapseAll : function(){
35427         this.root.collapse(true);
35428     },
35429
35430     /**
35431      * Returns the selection model used by this TreePanel
35432      */
35433     getSelectionModel : function(){
35434         if(!this.selModel){
35435             this.selModel = new Roo.tree.DefaultSelectionModel();
35436         }
35437         return this.selModel;
35438     },
35439
35440     /**
35441      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35442      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35443      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35444      * @return {Array}
35445      */
35446     getChecked : function(a, startNode){
35447         startNode = startNode || this.root;
35448         var r = [];
35449         var f = function(){
35450             if(this.attributes.checked){
35451                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35452             }
35453         }
35454         startNode.cascade(f);
35455         return r;
35456     },
35457
35458     /**
35459      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35460      * @param {String} path
35461      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35462      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35463      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35464      */
35465     expandPath : function(path, attr, callback){
35466         attr = attr || "id";
35467         var keys = path.split(this.pathSeparator);
35468         var curNode = this.root;
35469         if(curNode.attributes[attr] != keys[1]){ // invalid root
35470             if(callback){
35471                 callback(false, null);
35472             }
35473             return;
35474         }
35475         var index = 1;
35476         var f = function(){
35477             if(++index == keys.length){
35478                 if(callback){
35479                     callback(true, curNode);
35480                 }
35481                 return;
35482             }
35483             var c = curNode.findChild(attr, keys[index]);
35484             if(!c){
35485                 if(callback){
35486                     callback(false, curNode);
35487                 }
35488                 return;
35489             }
35490             curNode = c;
35491             c.expand(false, false, f);
35492         };
35493         curNode.expand(false, false, f);
35494     },
35495
35496     /**
35497      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35498      * @param {String} path
35499      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35500      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35501      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35502      */
35503     selectPath : function(path, attr, callback){
35504         attr = attr || "id";
35505         var keys = path.split(this.pathSeparator);
35506         var v = keys.pop();
35507         if(keys.length > 0){
35508             var f = function(success, node){
35509                 if(success && node){
35510                     var n = node.findChild(attr, v);
35511                     if(n){
35512                         n.select();
35513                         if(callback){
35514                             callback(true, n);
35515                         }
35516                     }else if(callback){
35517                         callback(false, n);
35518                     }
35519                 }else{
35520                     if(callback){
35521                         callback(false, n);
35522                     }
35523                 }
35524             };
35525             this.expandPath(keys.join(this.pathSeparator), attr, f);
35526         }else{
35527             this.root.select();
35528             if(callback){
35529                 callback(true, this.root);
35530             }
35531         }
35532     },
35533
35534     getTreeEl : function(){
35535         return this.el;
35536     },
35537
35538     /**
35539      * Trigger rendering of this TreePanel
35540      */
35541     render : function(){
35542         if (this.innerCt) {
35543             return this; // stop it rendering more than once!!
35544         }
35545         
35546         this.innerCt = this.el.createChild({tag:"ul",
35547                cls:"x-tree-root-ct " +
35548                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35549
35550         if(this.containerScroll){
35551             Roo.dd.ScrollManager.register(this.el);
35552         }
35553         if((this.enableDD || this.enableDrop) && !this.dropZone){
35554            /**
35555             * The dropZone used by this tree if drop is enabled
35556             * @type Roo.tree.TreeDropZone
35557             */
35558              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35559                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35560            });
35561         }
35562         if((this.enableDD || this.enableDrag) && !this.dragZone){
35563            /**
35564             * The dragZone used by this tree if drag is enabled
35565             * @type Roo.tree.TreeDragZone
35566             */
35567             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35568                ddGroup: this.ddGroup || "TreeDD",
35569                scroll: this.ddScroll
35570            });
35571         }
35572         this.getSelectionModel().init(this);
35573         if (!this.root) {
35574             Roo.log("ROOT not set in tree");
35575             return this;
35576         }
35577         this.root.render();
35578         if(!this.rootVisible){
35579             this.root.renderChildren();
35580         }
35581         return this;
35582     }
35583 });/*
35584  * Based on:
35585  * Ext JS Library 1.1.1
35586  * Copyright(c) 2006-2007, Ext JS, LLC.
35587  *
35588  * Originally Released Under LGPL - original licence link has changed is not relivant.
35589  *
35590  * Fork - LGPL
35591  * <script type="text/javascript">
35592  */
35593  
35594
35595 /**
35596  * @class Roo.tree.DefaultSelectionModel
35597  * @extends Roo.util.Observable
35598  * The default single selection for a TreePanel.
35599  * @param {Object} cfg Configuration
35600  */
35601 Roo.tree.DefaultSelectionModel = function(cfg){
35602    this.selNode = null;
35603    
35604    
35605    
35606    this.addEvents({
35607        /**
35608         * @event selectionchange
35609         * Fires when the selected node changes
35610         * @param {DefaultSelectionModel} this
35611         * @param {TreeNode} node the new selection
35612         */
35613        "selectionchange" : true,
35614
35615        /**
35616         * @event beforeselect
35617         * Fires before the selected node changes, return false to cancel the change
35618         * @param {DefaultSelectionModel} this
35619         * @param {TreeNode} node the new selection
35620         * @param {TreeNode} node the old selection
35621         */
35622        "beforeselect" : true
35623    });
35624    
35625     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35626 };
35627
35628 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35629     init : function(tree){
35630         this.tree = tree;
35631         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35632         tree.on("click", this.onNodeClick, this);
35633     },
35634     
35635     onNodeClick : function(node, e){
35636         if (e.ctrlKey && this.selNode == node)  {
35637             this.unselect(node);
35638             return;
35639         }
35640         this.select(node);
35641     },
35642     
35643     /**
35644      * Select a node.
35645      * @param {TreeNode} node The node to select
35646      * @return {TreeNode} The selected node
35647      */
35648     select : function(node){
35649         var last = this.selNode;
35650         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35651             if(last){
35652                 last.ui.onSelectedChange(false);
35653             }
35654             this.selNode = node;
35655             node.ui.onSelectedChange(true);
35656             this.fireEvent("selectionchange", this, node, last);
35657         }
35658         return node;
35659     },
35660     
35661     /**
35662      * Deselect a node.
35663      * @param {TreeNode} node The node to unselect
35664      */
35665     unselect : function(node){
35666         if(this.selNode == node){
35667             this.clearSelections();
35668         }    
35669     },
35670     
35671     /**
35672      * Clear all selections
35673      */
35674     clearSelections : function(){
35675         var n = this.selNode;
35676         if(n){
35677             n.ui.onSelectedChange(false);
35678             this.selNode = null;
35679             this.fireEvent("selectionchange", this, null);
35680         }
35681         return n;
35682     },
35683     
35684     /**
35685      * Get the selected node
35686      * @return {TreeNode} The selected node
35687      */
35688     getSelectedNode : function(){
35689         return this.selNode;    
35690     },
35691     
35692     /**
35693      * Returns true if the node is selected
35694      * @param {TreeNode} node The node to check
35695      * @return {Boolean}
35696      */
35697     isSelected : function(node){
35698         return this.selNode == node;  
35699     },
35700
35701     /**
35702      * Selects the node above the selected node in the tree, intelligently walking the nodes
35703      * @return TreeNode The new selection
35704      */
35705     selectPrevious : function(){
35706         var s = this.selNode || this.lastSelNode;
35707         if(!s){
35708             return null;
35709         }
35710         var ps = s.previousSibling;
35711         if(ps){
35712             if(!ps.isExpanded() || ps.childNodes.length < 1){
35713                 return this.select(ps);
35714             } else{
35715                 var lc = ps.lastChild;
35716                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35717                     lc = lc.lastChild;
35718                 }
35719                 return this.select(lc);
35720             }
35721         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35722             return this.select(s.parentNode);
35723         }
35724         return null;
35725     },
35726
35727     /**
35728      * Selects the node above the selected node in the tree, intelligently walking the nodes
35729      * @return TreeNode The new selection
35730      */
35731     selectNext : function(){
35732         var s = this.selNode || this.lastSelNode;
35733         if(!s){
35734             return null;
35735         }
35736         if(s.firstChild && s.isExpanded()){
35737              return this.select(s.firstChild);
35738          }else if(s.nextSibling){
35739              return this.select(s.nextSibling);
35740          }else if(s.parentNode){
35741             var newS = null;
35742             s.parentNode.bubble(function(){
35743                 if(this.nextSibling){
35744                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35745                     return false;
35746                 }
35747             });
35748             return newS;
35749          }
35750         return null;
35751     },
35752
35753     onKeyDown : function(e){
35754         var s = this.selNode || this.lastSelNode;
35755         // undesirable, but required
35756         var sm = this;
35757         if(!s){
35758             return;
35759         }
35760         var k = e.getKey();
35761         switch(k){
35762              case e.DOWN:
35763                  e.stopEvent();
35764                  this.selectNext();
35765              break;
35766              case e.UP:
35767                  e.stopEvent();
35768                  this.selectPrevious();
35769              break;
35770              case e.RIGHT:
35771                  e.preventDefault();
35772                  if(s.hasChildNodes()){
35773                      if(!s.isExpanded()){
35774                          s.expand();
35775                      }else if(s.firstChild){
35776                          this.select(s.firstChild, e);
35777                      }
35778                  }
35779              break;
35780              case e.LEFT:
35781                  e.preventDefault();
35782                  if(s.hasChildNodes() && s.isExpanded()){
35783                      s.collapse();
35784                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35785                      this.select(s.parentNode, e);
35786                  }
35787              break;
35788         };
35789     }
35790 });
35791
35792 /**
35793  * @class Roo.tree.MultiSelectionModel
35794  * @extends Roo.util.Observable
35795  * Multi selection for a TreePanel.
35796  * @param {Object} cfg Configuration
35797  */
35798 Roo.tree.MultiSelectionModel = function(){
35799    this.selNodes = [];
35800    this.selMap = {};
35801    this.addEvents({
35802        /**
35803         * @event selectionchange
35804         * Fires when the selected nodes change
35805         * @param {MultiSelectionModel} this
35806         * @param {Array} nodes Array of the selected nodes
35807         */
35808        "selectionchange" : true
35809    });
35810    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35811    
35812 };
35813
35814 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35815     init : function(tree){
35816         this.tree = tree;
35817         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35818         tree.on("click", this.onNodeClick, this);
35819     },
35820     
35821     onNodeClick : function(node, e){
35822         this.select(node, e, e.ctrlKey);
35823     },
35824     
35825     /**
35826      * Select a node.
35827      * @param {TreeNode} node The node to select
35828      * @param {EventObject} e (optional) An event associated with the selection
35829      * @param {Boolean} keepExisting True to retain existing selections
35830      * @return {TreeNode} The selected node
35831      */
35832     select : function(node, e, keepExisting){
35833         if(keepExisting !== true){
35834             this.clearSelections(true);
35835         }
35836         if(this.isSelected(node)){
35837             this.lastSelNode = node;
35838             return node;
35839         }
35840         this.selNodes.push(node);
35841         this.selMap[node.id] = node;
35842         this.lastSelNode = node;
35843         node.ui.onSelectedChange(true);
35844         this.fireEvent("selectionchange", this, this.selNodes);
35845         return node;
35846     },
35847     
35848     /**
35849      * Deselect a node.
35850      * @param {TreeNode} node The node to unselect
35851      */
35852     unselect : function(node){
35853         if(this.selMap[node.id]){
35854             node.ui.onSelectedChange(false);
35855             var sn = this.selNodes;
35856             var index = -1;
35857             if(sn.indexOf){
35858                 index = sn.indexOf(node);
35859             }else{
35860                 for(var i = 0, len = sn.length; i < len; i++){
35861                     if(sn[i] == node){
35862                         index = i;
35863                         break;
35864                     }
35865                 }
35866             }
35867             if(index != -1){
35868                 this.selNodes.splice(index, 1);
35869             }
35870             delete this.selMap[node.id];
35871             this.fireEvent("selectionchange", this, this.selNodes);
35872         }
35873     },
35874     
35875     /**
35876      * Clear all selections
35877      */
35878     clearSelections : function(suppressEvent){
35879         var sn = this.selNodes;
35880         if(sn.length > 0){
35881             for(var i = 0, len = sn.length; i < len; i++){
35882                 sn[i].ui.onSelectedChange(false);
35883             }
35884             this.selNodes = [];
35885             this.selMap = {};
35886             if(suppressEvent !== true){
35887                 this.fireEvent("selectionchange", this, this.selNodes);
35888             }
35889         }
35890     },
35891     
35892     /**
35893      * Returns true if the node is selected
35894      * @param {TreeNode} node The node to check
35895      * @return {Boolean}
35896      */
35897     isSelected : function(node){
35898         return this.selMap[node.id] ? true : false;  
35899     },
35900     
35901     /**
35902      * Returns an array of the selected nodes
35903      * @return {Array}
35904      */
35905     getSelectedNodes : function(){
35906         return this.selNodes;    
35907     },
35908
35909     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35910
35911     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35912
35913     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35914 });/*
35915  * Based on:
35916  * Ext JS Library 1.1.1
35917  * Copyright(c) 2006-2007, Ext JS, LLC.
35918  *
35919  * Originally Released Under LGPL - original licence link has changed is not relivant.
35920  *
35921  * Fork - LGPL
35922  * <script type="text/javascript">
35923  */
35924  
35925 /**
35926  * @class Roo.tree.TreeNode
35927  * @extends Roo.data.Node
35928  * @cfg {String} text The text for this node
35929  * @cfg {Boolean} expanded true to start the node expanded
35930  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35931  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35932  * @cfg {Boolean} disabled true to start the node disabled
35933  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35934  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35935  * @cfg {String} cls A css class to be added to the node
35936  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35937  * @cfg {String} href URL of the link used for the node (defaults to #)
35938  * @cfg {String} hrefTarget target frame for the link
35939  * @cfg {String} qtip An Ext QuickTip for the node
35940  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35941  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35942  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35943  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35944  * (defaults to undefined with no checkbox rendered)
35945  * @constructor
35946  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35947  */
35948 Roo.tree.TreeNode = function(attributes){
35949     attributes = attributes || {};
35950     if(typeof attributes == "string"){
35951         attributes = {text: attributes};
35952     }
35953     this.childrenRendered = false;
35954     this.rendered = false;
35955     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35956     this.expanded = attributes.expanded === true;
35957     this.isTarget = attributes.isTarget !== false;
35958     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35959     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35960
35961     /**
35962      * Read-only. The text for this node. To change it use setText().
35963      * @type String
35964      */
35965     this.text = attributes.text;
35966     /**
35967      * True if this node is disabled.
35968      * @type Boolean
35969      */
35970     this.disabled = attributes.disabled === true;
35971
35972     this.addEvents({
35973         /**
35974         * @event textchange
35975         * Fires when the text for this node is changed
35976         * @param {Node} this This node
35977         * @param {String} text The new text
35978         * @param {String} oldText The old text
35979         */
35980         "textchange" : true,
35981         /**
35982         * @event beforeexpand
35983         * Fires before this node is expanded, return false to cancel.
35984         * @param {Node} this This node
35985         * @param {Boolean} deep
35986         * @param {Boolean} anim
35987         */
35988         "beforeexpand" : true,
35989         /**
35990         * @event beforecollapse
35991         * Fires before this node is collapsed, return false to cancel.
35992         * @param {Node} this This node
35993         * @param {Boolean} deep
35994         * @param {Boolean} anim
35995         */
35996         "beforecollapse" : true,
35997         /**
35998         * @event expand
35999         * Fires when this node is expanded
36000         * @param {Node} this This node
36001         */
36002         "expand" : true,
36003         /**
36004         * @event disabledchange
36005         * Fires when the disabled status of this node changes
36006         * @param {Node} this This node
36007         * @param {Boolean} disabled
36008         */
36009         "disabledchange" : true,
36010         /**
36011         * @event collapse
36012         * Fires when this node is collapsed
36013         * @param {Node} this This node
36014         */
36015         "collapse" : true,
36016         /**
36017         * @event beforeclick
36018         * Fires before click processing. Return false to cancel the default action.
36019         * @param {Node} this This node
36020         * @param {Roo.EventObject} e The event object
36021         */
36022         "beforeclick":true,
36023         /**
36024         * @event checkchange
36025         * Fires when a node with a checkbox's checked property changes
36026         * @param {Node} this This node
36027         * @param {Boolean} checked
36028         */
36029         "checkchange":true,
36030         /**
36031         * @event click
36032         * Fires when this node is clicked
36033         * @param {Node} this This node
36034         * @param {Roo.EventObject} e The event object
36035         */
36036         "click":true,
36037         /**
36038         * @event dblclick
36039         * Fires when this node is double clicked
36040         * @param {Node} this This node
36041         * @param {Roo.EventObject} e The event object
36042         */
36043         "dblclick":true,
36044         /**
36045         * @event contextmenu
36046         * Fires when this node is right clicked
36047         * @param {Node} this This node
36048         * @param {Roo.EventObject} e The event object
36049         */
36050         "contextmenu":true,
36051         /**
36052         * @event beforechildrenrendered
36053         * Fires right before the child nodes for this node are rendered
36054         * @param {Node} this This node
36055         */
36056         "beforechildrenrendered":true
36057     });
36058
36059     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36060
36061     /**
36062      * Read-only. The UI for this node
36063      * @type TreeNodeUI
36064      */
36065     this.ui = new uiClass(this);
36066     
36067     // finally support items[]
36068     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36069         return;
36070     }
36071     
36072     
36073     Roo.each(this.attributes.items, function(c) {
36074         this.appendChild(Roo.factory(c,Roo.Tree));
36075     }, this);
36076     delete this.attributes.items;
36077     
36078     
36079     
36080 };
36081 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36082     preventHScroll: true,
36083     /**
36084      * Returns true if this node is expanded
36085      * @return {Boolean}
36086      */
36087     isExpanded : function(){
36088         return this.expanded;
36089     },
36090
36091     /**
36092      * Returns the UI object for this node
36093      * @return {TreeNodeUI}
36094      */
36095     getUI : function(){
36096         return this.ui;
36097     },
36098
36099     // private override
36100     setFirstChild : function(node){
36101         var of = this.firstChild;
36102         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36103         if(this.childrenRendered && of && node != of){
36104             of.renderIndent(true, true);
36105         }
36106         if(this.rendered){
36107             this.renderIndent(true, true);
36108         }
36109     },
36110
36111     // private override
36112     setLastChild : function(node){
36113         var ol = this.lastChild;
36114         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36115         if(this.childrenRendered && ol && node != ol){
36116             ol.renderIndent(true, true);
36117         }
36118         if(this.rendered){
36119             this.renderIndent(true, true);
36120         }
36121     },
36122
36123     // these methods are overridden to provide lazy rendering support
36124     // private override
36125     appendChild : function()
36126     {
36127         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36128         if(node && this.childrenRendered){
36129             node.render();
36130         }
36131         this.ui.updateExpandIcon();
36132         return node;
36133     },
36134
36135     // private override
36136     removeChild : function(node){
36137         this.ownerTree.getSelectionModel().unselect(node);
36138         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36139         // if it's been rendered remove dom node
36140         if(this.childrenRendered){
36141             node.ui.remove();
36142         }
36143         if(this.childNodes.length < 1){
36144             this.collapse(false, false);
36145         }else{
36146             this.ui.updateExpandIcon();
36147         }
36148         if(!this.firstChild) {
36149             this.childrenRendered = false;
36150         }
36151         return node;
36152     },
36153
36154     // private override
36155     insertBefore : function(node, refNode){
36156         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36157         if(newNode && refNode && this.childrenRendered){
36158             node.render();
36159         }
36160         this.ui.updateExpandIcon();
36161         return newNode;
36162     },
36163
36164     /**
36165      * Sets the text for this node
36166      * @param {String} text
36167      */
36168     setText : function(text){
36169         var oldText = this.text;
36170         this.text = text;
36171         this.attributes.text = text;
36172         if(this.rendered){ // event without subscribing
36173             this.ui.onTextChange(this, text, oldText);
36174         }
36175         this.fireEvent("textchange", this, text, oldText);
36176     },
36177
36178     /**
36179      * Triggers selection of this node
36180      */
36181     select : function(){
36182         this.getOwnerTree().getSelectionModel().select(this);
36183     },
36184
36185     /**
36186      * Triggers deselection of this node
36187      */
36188     unselect : function(){
36189         this.getOwnerTree().getSelectionModel().unselect(this);
36190     },
36191
36192     /**
36193      * Returns true if this node is selected
36194      * @return {Boolean}
36195      */
36196     isSelected : function(){
36197         return this.getOwnerTree().getSelectionModel().isSelected(this);
36198     },
36199
36200     /**
36201      * Expand this node.
36202      * @param {Boolean} deep (optional) True to expand all children as well
36203      * @param {Boolean} anim (optional) false to cancel the default animation
36204      * @param {Function} callback (optional) A callback to be called when
36205      * expanding this node completes (does not wait for deep expand to complete).
36206      * Called with 1 parameter, this node.
36207      */
36208     expand : function(deep, anim, callback){
36209         if(!this.expanded){
36210             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36211                 return;
36212             }
36213             if(!this.childrenRendered){
36214                 this.renderChildren();
36215             }
36216             this.expanded = true;
36217             
36218             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36219                 this.ui.animExpand(function(){
36220                     this.fireEvent("expand", this);
36221                     if(typeof callback == "function"){
36222                         callback(this);
36223                     }
36224                     if(deep === true){
36225                         this.expandChildNodes(true);
36226                     }
36227                 }.createDelegate(this));
36228                 return;
36229             }else{
36230                 this.ui.expand();
36231                 this.fireEvent("expand", this);
36232                 if(typeof callback == "function"){
36233                     callback(this);
36234                 }
36235             }
36236         }else{
36237            if(typeof callback == "function"){
36238                callback(this);
36239            }
36240         }
36241         if(deep === true){
36242             this.expandChildNodes(true);
36243         }
36244     },
36245
36246     isHiddenRoot : function(){
36247         return this.isRoot && !this.getOwnerTree().rootVisible;
36248     },
36249
36250     /**
36251      * Collapse this node.
36252      * @param {Boolean} deep (optional) True to collapse all children as well
36253      * @param {Boolean} anim (optional) false to cancel the default animation
36254      */
36255     collapse : function(deep, anim){
36256         if(this.expanded && !this.isHiddenRoot()){
36257             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36258                 return;
36259             }
36260             this.expanded = false;
36261             if((this.getOwnerTree().animate && anim !== false) || anim){
36262                 this.ui.animCollapse(function(){
36263                     this.fireEvent("collapse", this);
36264                     if(deep === true){
36265                         this.collapseChildNodes(true);
36266                     }
36267                 }.createDelegate(this));
36268                 return;
36269             }else{
36270                 this.ui.collapse();
36271                 this.fireEvent("collapse", this);
36272             }
36273         }
36274         if(deep === true){
36275             var cs = this.childNodes;
36276             for(var i = 0, len = cs.length; i < len; i++) {
36277                 cs[i].collapse(true, false);
36278             }
36279         }
36280     },
36281
36282     // private
36283     delayedExpand : function(delay){
36284         if(!this.expandProcId){
36285             this.expandProcId = this.expand.defer(delay, this);
36286         }
36287     },
36288
36289     // private
36290     cancelExpand : function(){
36291         if(this.expandProcId){
36292             clearTimeout(this.expandProcId);
36293         }
36294         this.expandProcId = false;
36295     },
36296
36297     /**
36298      * Toggles expanded/collapsed state of the node
36299      */
36300     toggle : function(){
36301         if(this.expanded){
36302             this.collapse();
36303         }else{
36304             this.expand();
36305         }
36306     },
36307
36308     /**
36309      * Ensures all parent nodes are expanded
36310      */
36311     ensureVisible : function(callback){
36312         var tree = this.getOwnerTree();
36313         tree.expandPath(this.parentNode.getPath(), false, function(){
36314             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36315             Roo.callback(callback);
36316         }.createDelegate(this));
36317     },
36318
36319     /**
36320      * Expand all child nodes
36321      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36322      */
36323     expandChildNodes : function(deep){
36324         var cs = this.childNodes;
36325         for(var i = 0, len = cs.length; i < len; i++) {
36326                 cs[i].expand(deep);
36327         }
36328     },
36329
36330     /**
36331      * Collapse all child nodes
36332      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36333      */
36334     collapseChildNodes : function(deep){
36335         var cs = this.childNodes;
36336         for(var i = 0, len = cs.length; i < len; i++) {
36337                 cs[i].collapse(deep);
36338         }
36339     },
36340
36341     /**
36342      * Disables this node
36343      */
36344     disable : function(){
36345         this.disabled = true;
36346         this.unselect();
36347         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36348             this.ui.onDisableChange(this, true);
36349         }
36350         this.fireEvent("disabledchange", this, true);
36351     },
36352
36353     /**
36354      * Enables this node
36355      */
36356     enable : function(){
36357         this.disabled = false;
36358         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36359             this.ui.onDisableChange(this, false);
36360         }
36361         this.fireEvent("disabledchange", this, false);
36362     },
36363
36364     // private
36365     renderChildren : function(suppressEvent){
36366         if(suppressEvent !== false){
36367             this.fireEvent("beforechildrenrendered", this);
36368         }
36369         var cs = this.childNodes;
36370         for(var i = 0, len = cs.length; i < len; i++){
36371             cs[i].render(true);
36372         }
36373         this.childrenRendered = true;
36374     },
36375
36376     // private
36377     sort : function(fn, scope){
36378         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36379         if(this.childrenRendered){
36380             var cs = this.childNodes;
36381             for(var i = 0, len = cs.length; i < len; i++){
36382                 cs[i].render(true);
36383             }
36384         }
36385     },
36386
36387     // private
36388     render : function(bulkRender){
36389         this.ui.render(bulkRender);
36390         if(!this.rendered){
36391             this.rendered = true;
36392             if(this.expanded){
36393                 this.expanded = false;
36394                 this.expand(false, false);
36395             }
36396         }
36397     },
36398
36399     // private
36400     renderIndent : function(deep, refresh){
36401         if(refresh){
36402             this.ui.childIndent = null;
36403         }
36404         this.ui.renderIndent();
36405         if(deep === true && this.childrenRendered){
36406             var cs = this.childNodes;
36407             for(var i = 0, len = cs.length; i < len; i++){
36408                 cs[i].renderIndent(true, refresh);
36409             }
36410         }
36411     }
36412 });/*
36413  * Based on:
36414  * Ext JS Library 1.1.1
36415  * Copyright(c) 2006-2007, Ext JS, LLC.
36416  *
36417  * Originally Released Under LGPL - original licence link has changed is not relivant.
36418  *
36419  * Fork - LGPL
36420  * <script type="text/javascript">
36421  */
36422  
36423 /**
36424  * @class Roo.tree.AsyncTreeNode
36425  * @extends Roo.tree.TreeNode
36426  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36427  * @constructor
36428  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36429  */
36430  Roo.tree.AsyncTreeNode = function(config){
36431     this.loaded = false;
36432     this.loading = false;
36433     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36434     /**
36435     * @event beforeload
36436     * Fires before this node is loaded, return false to cancel
36437     * @param {Node} this This node
36438     */
36439     this.addEvents({'beforeload':true, 'load': true});
36440     /**
36441     * @event load
36442     * Fires when this node is loaded
36443     * @param {Node} this This node
36444     */
36445     /**
36446      * The loader used by this node (defaults to using the tree's defined loader)
36447      * @type TreeLoader
36448      * @property loader
36449      */
36450 };
36451 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36452     expand : function(deep, anim, callback){
36453         if(this.loading){ // if an async load is already running, waiting til it's done
36454             var timer;
36455             var f = function(){
36456                 if(!this.loading){ // done loading
36457                     clearInterval(timer);
36458                     this.expand(deep, anim, callback);
36459                 }
36460             }.createDelegate(this);
36461             timer = setInterval(f, 200);
36462             return;
36463         }
36464         if(!this.loaded){
36465             if(this.fireEvent("beforeload", this) === false){
36466                 return;
36467             }
36468             this.loading = true;
36469             this.ui.beforeLoad(this);
36470             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36471             if(loader){
36472                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36473                 return;
36474             }
36475         }
36476         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36477     },
36478     
36479     /**
36480      * Returns true if this node is currently loading
36481      * @return {Boolean}
36482      */
36483     isLoading : function(){
36484         return this.loading;  
36485     },
36486     
36487     loadComplete : function(deep, anim, callback){
36488         this.loading = false;
36489         this.loaded = true;
36490         this.ui.afterLoad(this);
36491         this.fireEvent("load", this);
36492         this.expand(deep, anim, callback);
36493     },
36494     
36495     /**
36496      * Returns true if this node has been loaded
36497      * @return {Boolean}
36498      */
36499     isLoaded : function(){
36500         return this.loaded;
36501     },
36502     
36503     hasChildNodes : function(){
36504         if(!this.isLeaf() && !this.loaded){
36505             return true;
36506         }else{
36507             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36508         }
36509     },
36510
36511     /**
36512      * Trigger a reload for this node
36513      * @param {Function} callback
36514      */
36515     reload : function(callback){
36516         this.collapse(false, false);
36517         while(this.firstChild){
36518             this.removeChild(this.firstChild);
36519         }
36520         this.childrenRendered = false;
36521         this.loaded = false;
36522         if(this.isHiddenRoot()){
36523             this.expanded = false;
36524         }
36525         this.expand(false, false, callback);
36526     }
36527 });/*
36528  * Based on:
36529  * Ext JS Library 1.1.1
36530  * Copyright(c) 2006-2007, Ext JS, LLC.
36531  *
36532  * Originally Released Under LGPL - original licence link has changed is not relivant.
36533  *
36534  * Fork - LGPL
36535  * <script type="text/javascript">
36536  */
36537  
36538 /**
36539  * @class Roo.tree.TreeNodeUI
36540  * @constructor
36541  * @param {Object} node The node to render
36542  * The TreeNode UI implementation is separate from the
36543  * tree implementation. Unless you are customizing the tree UI,
36544  * you should never have to use this directly.
36545  */
36546 Roo.tree.TreeNodeUI = function(node){
36547     this.node = node;
36548     this.rendered = false;
36549     this.animating = false;
36550     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36551 };
36552
36553 Roo.tree.TreeNodeUI.prototype = {
36554     removeChild : function(node){
36555         if(this.rendered){
36556             this.ctNode.removeChild(node.ui.getEl());
36557         }
36558     },
36559
36560     beforeLoad : function(){
36561          this.addClass("x-tree-node-loading");
36562     },
36563
36564     afterLoad : function(){
36565          this.removeClass("x-tree-node-loading");
36566     },
36567
36568     onTextChange : function(node, text, oldText){
36569         if(this.rendered){
36570             this.textNode.innerHTML = text;
36571         }
36572     },
36573
36574     onDisableChange : function(node, state){
36575         this.disabled = state;
36576         if(state){
36577             this.addClass("x-tree-node-disabled");
36578         }else{
36579             this.removeClass("x-tree-node-disabled");
36580         }
36581     },
36582
36583     onSelectedChange : function(state){
36584         if(state){
36585             this.focus();
36586             this.addClass("x-tree-selected");
36587         }else{
36588             //this.blur();
36589             this.removeClass("x-tree-selected");
36590         }
36591     },
36592
36593     onMove : function(tree, node, oldParent, newParent, index, refNode){
36594         this.childIndent = null;
36595         if(this.rendered){
36596             var targetNode = newParent.ui.getContainer();
36597             if(!targetNode){//target not rendered
36598                 this.holder = document.createElement("div");
36599                 this.holder.appendChild(this.wrap);
36600                 return;
36601             }
36602             var insertBefore = refNode ? refNode.ui.getEl() : null;
36603             if(insertBefore){
36604                 targetNode.insertBefore(this.wrap, insertBefore);
36605             }else{
36606                 targetNode.appendChild(this.wrap);
36607             }
36608             this.node.renderIndent(true);
36609         }
36610     },
36611
36612     addClass : function(cls){
36613         if(this.elNode){
36614             Roo.fly(this.elNode).addClass(cls);
36615         }
36616     },
36617
36618     removeClass : function(cls){
36619         if(this.elNode){
36620             Roo.fly(this.elNode).removeClass(cls);
36621         }
36622     },
36623
36624     remove : function(){
36625         if(this.rendered){
36626             this.holder = document.createElement("div");
36627             this.holder.appendChild(this.wrap);
36628         }
36629     },
36630
36631     fireEvent : function(){
36632         return this.node.fireEvent.apply(this.node, arguments);
36633     },
36634
36635     initEvents : function(){
36636         this.node.on("move", this.onMove, this);
36637         var E = Roo.EventManager;
36638         var a = this.anchor;
36639
36640         var el = Roo.fly(a, '_treeui');
36641
36642         if(Roo.isOpera){ // opera render bug ignores the CSS
36643             el.setStyle("text-decoration", "none");
36644         }
36645
36646         el.on("click", this.onClick, this);
36647         el.on("dblclick", this.onDblClick, this);
36648
36649         if(this.checkbox){
36650             Roo.EventManager.on(this.checkbox,
36651                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36652         }
36653
36654         el.on("contextmenu", this.onContextMenu, this);
36655
36656         var icon = Roo.fly(this.iconNode);
36657         icon.on("click", this.onClick, this);
36658         icon.on("dblclick", this.onDblClick, this);
36659         icon.on("contextmenu", this.onContextMenu, this);
36660         E.on(this.ecNode, "click", this.ecClick, this, true);
36661
36662         if(this.node.disabled){
36663             this.addClass("x-tree-node-disabled");
36664         }
36665         if(this.node.hidden){
36666             this.addClass("x-tree-node-disabled");
36667         }
36668         var ot = this.node.getOwnerTree();
36669         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36670         if(dd && (!this.node.isRoot || ot.rootVisible)){
36671             Roo.dd.Registry.register(this.elNode, {
36672                 node: this.node,
36673                 handles: this.getDDHandles(),
36674                 isHandle: false
36675             });
36676         }
36677     },
36678
36679     getDDHandles : function(){
36680         return [this.iconNode, this.textNode];
36681     },
36682
36683     hide : function(){
36684         if(this.rendered){
36685             this.wrap.style.display = "none";
36686         }
36687     },
36688
36689     show : function(){
36690         if(this.rendered){
36691             this.wrap.style.display = "";
36692         }
36693     },
36694
36695     onContextMenu : function(e){
36696         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36697             e.preventDefault();
36698             this.focus();
36699             this.fireEvent("contextmenu", this.node, e);
36700         }
36701     },
36702
36703     onClick : function(e){
36704         if(this.dropping){
36705             e.stopEvent();
36706             return;
36707         }
36708         if(this.fireEvent("beforeclick", this.node, e) !== false){
36709             if(!this.disabled && this.node.attributes.href){
36710                 this.fireEvent("click", this.node, e);
36711                 return;
36712             }
36713             e.preventDefault();
36714             if(this.disabled){
36715                 return;
36716             }
36717
36718             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36719                 this.node.toggle();
36720             }
36721
36722             this.fireEvent("click", this.node, e);
36723         }else{
36724             e.stopEvent();
36725         }
36726     },
36727
36728     onDblClick : function(e){
36729         e.preventDefault();
36730         if(this.disabled){
36731             return;
36732         }
36733         if(this.checkbox){
36734             this.toggleCheck();
36735         }
36736         if(!this.animating && this.node.hasChildNodes()){
36737             this.node.toggle();
36738         }
36739         this.fireEvent("dblclick", this.node, e);
36740     },
36741
36742     onCheckChange : function(){
36743         var checked = this.checkbox.checked;
36744         this.node.attributes.checked = checked;
36745         this.fireEvent('checkchange', this.node, checked);
36746     },
36747
36748     ecClick : function(e){
36749         if(!this.animating && this.node.hasChildNodes()){
36750             this.node.toggle();
36751         }
36752     },
36753
36754     startDrop : function(){
36755         this.dropping = true;
36756     },
36757
36758     // delayed drop so the click event doesn't get fired on a drop
36759     endDrop : function(){
36760        setTimeout(function(){
36761            this.dropping = false;
36762        }.createDelegate(this), 50);
36763     },
36764
36765     expand : function(){
36766         this.updateExpandIcon();
36767         this.ctNode.style.display = "";
36768     },
36769
36770     focus : function(){
36771         if(!this.node.preventHScroll){
36772             try{this.anchor.focus();
36773             }catch(e){}
36774         }else if(!Roo.isIE){
36775             try{
36776                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36777                 var l = noscroll.scrollLeft;
36778                 this.anchor.focus();
36779                 noscroll.scrollLeft = l;
36780             }catch(e){}
36781         }
36782     },
36783
36784     toggleCheck : function(value){
36785         var cb = this.checkbox;
36786         if(cb){
36787             cb.checked = (value === undefined ? !cb.checked : value);
36788         }
36789     },
36790
36791     blur : function(){
36792         try{
36793             this.anchor.blur();
36794         }catch(e){}
36795     },
36796
36797     animExpand : function(callback){
36798         var ct = Roo.get(this.ctNode);
36799         ct.stopFx();
36800         if(!this.node.hasChildNodes()){
36801             this.updateExpandIcon();
36802             this.ctNode.style.display = "";
36803             Roo.callback(callback);
36804             return;
36805         }
36806         this.animating = true;
36807         this.updateExpandIcon();
36808
36809         ct.slideIn('t', {
36810            callback : function(){
36811                this.animating = false;
36812                Roo.callback(callback);
36813             },
36814             scope: this,
36815             duration: this.node.ownerTree.duration || .25
36816         });
36817     },
36818
36819     highlight : function(){
36820         var tree = this.node.getOwnerTree();
36821         Roo.fly(this.wrap).highlight(
36822             tree.hlColor || "C3DAF9",
36823             {endColor: tree.hlBaseColor}
36824         );
36825     },
36826
36827     collapse : function(){
36828         this.updateExpandIcon();
36829         this.ctNode.style.display = "none";
36830     },
36831
36832     animCollapse : function(callback){
36833         var ct = Roo.get(this.ctNode);
36834         ct.enableDisplayMode('block');
36835         ct.stopFx();
36836
36837         this.animating = true;
36838         this.updateExpandIcon();
36839
36840         ct.slideOut('t', {
36841             callback : function(){
36842                this.animating = false;
36843                Roo.callback(callback);
36844             },
36845             scope: this,
36846             duration: this.node.ownerTree.duration || .25
36847         });
36848     },
36849
36850     getContainer : function(){
36851         return this.ctNode;
36852     },
36853
36854     getEl : function(){
36855         return this.wrap;
36856     },
36857
36858     appendDDGhost : function(ghostNode){
36859         ghostNode.appendChild(this.elNode.cloneNode(true));
36860     },
36861
36862     getDDRepairXY : function(){
36863         return Roo.lib.Dom.getXY(this.iconNode);
36864     },
36865
36866     onRender : function(){
36867         this.render();
36868     },
36869
36870     render : function(bulkRender){
36871         var n = this.node, a = n.attributes;
36872         var targetNode = n.parentNode ?
36873               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36874
36875         if(!this.rendered){
36876             this.rendered = true;
36877
36878             this.renderElements(n, a, targetNode, bulkRender);
36879
36880             if(a.qtip){
36881                if(this.textNode.setAttributeNS){
36882                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36883                    if(a.qtipTitle){
36884                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36885                    }
36886                }else{
36887                    this.textNode.setAttribute("ext:qtip", a.qtip);
36888                    if(a.qtipTitle){
36889                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36890                    }
36891                }
36892             }else if(a.qtipCfg){
36893                 a.qtipCfg.target = Roo.id(this.textNode);
36894                 Roo.QuickTips.register(a.qtipCfg);
36895             }
36896             this.initEvents();
36897             if(!this.node.expanded){
36898                 this.updateExpandIcon();
36899             }
36900         }else{
36901             if(bulkRender === true) {
36902                 targetNode.appendChild(this.wrap);
36903             }
36904         }
36905     },
36906
36907     renderElements : function(n, a, targetNode, bulkRender)
36908     {
36909         // add some indent caching, this helps performance when rendering a large tree
36910         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36911         var t = n.getOwnerTree();
36912         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36913         if (typeof(n.attributes.html) != 'undefined') {
36914             txt = n.attributes.html;
36915         }
36916         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36917         var cb = typeof a.checked == 'boolean';
36918         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36919         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36920             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36921             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36922             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36923             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36924             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36925              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36926                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36927             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36928             "</li>"];
36929
36930         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36931             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36932                                 n.nextSibling.ui.getEl(), buf.join(""));
36933         }else{
36934             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36935         }
36936
36937         this.elNode = this.wrap.childNodes[0];
36938         this.ctNode = this.wrap.childNodes[1];
36939         var cs = this.elNode.childNodes;
36940         this.indentNode = cs[0];
36941         this.ecNode = cs[1];
36942         this.iconNode = cs[2];
36943         var index = 3;
36944         if(cb){
36945             this.checkbox = cs[3];
36946             index++;
36947         }
36948         this.anchor = cs[index];
36949         this.textNode = cs[index].firstChild;
36950     },
36951
36952     getAnchor : function(){
36953         return this.anchor;
36954     },
36955
36956     getTextEl : function(){
36957         return this.textNode;
36958     },
36959
36960     getIconEl : function(){
36961         return this.iconNode;
36962     },
36963
36964     isChecked : function(){
36965         return this.checkbox ? this.checkbox.checked : false;
36966     },
36967
36968     updateExpandIcon : function(){
36969         if(this.rendered){
36970             var n = this.node, c1, c2;
36971             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36972             var hasChild = n.hasChildNodes();
36973             if(hasChild){
36974                 if(n.expanded){
36975                     cls += "-minus";
36976                     c1 = "x-tree-node-collapsed";
36977                     c2 = "x-tree-node-expanded";
36978                 }else{
36979                     cls += "-plus";
36980                     c1 = "x-tree-node-expanded";
36981                     c2 = "x-tree-node-collapsed";
36982                 }
36983                 if(this.wasLeaf){
36984                     this.removeClass("x-tree-node-leaf");
36985                     this.wasLeaf = false;
36986                 }
36987                 if(this.c1 != c1 || this.c2 != c2){
36988                     Roo.fly(this.elNode).replaceClass(c1, c2);
36989                     this.c1 = c1; this.c2 = c2;
36990                 }
36991             }else{
36992                 // this changes non-leafs into leafs if they have no children.
36993                 // it's not very rational behaviour..
36994                 
36995                 if(!this.wasLeaf && this.node.leaf){
36996                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36997                     delete this.c1;
36998                     delete this.c2;
36999                     this.wasLeaf = true;
37000                 }
37001             }
37002             var ecc = "x-tree-ec-icon "+cls;
37003             if(this.ecc != ecc){
37004                 this.ecNode.className = ecc;
37005                 this.ecc = ecc;
37006             }
37007         }
37008     },
37009
37010     getChildIndent : function(){
37011         if(!this.childIndent){
37012             var buf = [];
37013             var p = this.node;
37014             while(p){
37015                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37016                     if(!p.isLast()) {
37017                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37018                     } else {
37019                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37020                     }
37021                 }
37022                 p = p.parentNode;
37023             }
37024             this.childIndent = buf.join("");
37025         }
37026         return this.childIndent;
37027     },
37028
37029     renderIndent : function(){
37030         if(this.rendered){
37031             var indent = "";
37032             var p = this.node.parentNode;
37033             if(p){
37034                 indent = p.ui.getChildIndent();
37035             }
37036             if(this.indentMarkup != indent){ // don't rerender if not required
37037                 this.indentNode.innerHTML = indent;
37038                 this.indentMarkup = indent;
37039             }
37040             this.updateExpandIcon();
37041         }
37042     }
37043 };
37044
37045 Roo.tree.RootTreeNodeUI = function(){
37046     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37047 };
37048 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37049     render : function(){
37050         if(!this.rendered){
37051             var targetNode = this.node.ownerTree.innerCt.dom;
37052             this.node.expanded = true;
37053             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37054             this.wrap = this.ctNode = targetNode.firstChild;
37055         }
37056     },
37057     collapse : function(){
37058     },
37059     expand : function(){
37060     }
37061 });/*
37062  * Based on:
37063  * Ext JS Library 1.1.1
37064  * Copyright(c) 2006-2007, Ext JS, LLC.
37065  *
37066  * Originally Released Under LGPL - original licence link has changed is not relivant.
37067  *
37068  * Fork - LGPL
37069  * <script type="text/javascript">
37070  */
37071 /**
37072  * @class Roo.tree.TreeLoader
37073  * @extends Roo.util.Observable
37074  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37075  * nodes from a specified URL. The response must be a javascript Array definition
37076  * who's elements are node definition objects. eg:
37077  * <pre><code>
37078 {  success : true,
37079    data :      [
37080    
37081     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37082     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37083     ]
37084 }
37085
37086
37087 </code></pre>
37088  * <br><br>
37089  * The old style respose with just an array is still supported, but not recommended.
37090  * <br><br>
37091  *
37092  * A server request is sent, and child nodes are loaded only when a node is expanded.
37093  * The loading node's id is passed to the server under the parameter name "node" to
37094  * enable the server to produce the correct child nodes.
37095  * <br><br>
37096  * To pass extra parameters, an event handler may be attached to the "beforeload"
37097  * event, and the parameters specified in the TreeLoader's baseParams property:
37098  * <pre><code>
37099     myTreeLoader.on("beforeload", function(treeLoader, node) {
37100         this.baseParams.category = node.attributes.category;
37101     }, this);
37102     
37103 </code></pre>
37104  *
37105  * This would pass an HTTP parameter called "category" to the server containing
37106  * the value of the Node's "category" attribute.
37107  * @constructor
37108  * Creates a new Treeloader.
37109  * @param {Object} config A config object containing config properties.
37110  */
37111 Roo.tree.TreeLoader = function(config){
37112     this.baseParams = {};
37113     this.requestMethod = "POST";
37114     Roo.apply(this, config);
37115
37116     this.addEvents({
37117     
37118         /**
37119          * @event beforeload
37120          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37121          * @param {Object} This TreeLoader object.
37122          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37123          * @param {Object} callback The callback function specified in the {@link #load} call.
37124          */
37125         beforeload : true,
37126         /**
37127          * @event load
37128          * Fires when the node has been successfuly loaded.
37129          * @param {Object} This TreeLoader object.
37130          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37131          * @param {Object} response The response object containing the data from the server.
37132          */
37133         load : true,
37134         /**
37135          * @event loadexception
37136          * Fires if the network request failed.
37137          * @param {Object} This TreeLoader object.
37138          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37139          * @param {Object} response The response object containing the data from the server.
37140          */
37141         loadexception : true,
37142         /**
37143          * @event create
37144          * Fires before a node is created, enabling you to return custom Node types 
37145          * @param {Object} This TreeLoader object.
37146          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37147          */
37148         create : true
37149     });
37150
37151     Roo.tree.TreeLoader.superclass.constructor.call(this);
37152 };
37153
37154 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37155     /**
37156     * @cfg {String} dataUrl The URL from which to request a Json string which
37157     * specifies an array of node definition object representing the child nodes
37158     * to be loaded.
37159     */
37160     /**
37161     * @cfg {String} requestMethod either GET or POST
37162     * defaults to POST (due to BC)
37163     * to be loaded.
37164     */
37165     /**
37166     * @cfg {Object} baseParams (optional) An object containing properties which
37167     * specify HTTP parameters to be passed to each request for child nodes.
37168     */
37169     /**
37170     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37171     * created by this loader. If the attributes sent by the server have an attribute in this object,
37172     * they take priority.
37173     */
37174     /**
37175     * @cfg {Object} uiProviders (optional) An object containing properties which
37176     * 
37177     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37178     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37179     * <i>uiProvider</i> attribute of a returned child node is a string rather
37180     * than a reference to a TreeNodeUI implementation, this that string value
37181     * is used as a property name in the uiProviders object. You can define the provider named
37182     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37183     */
37184     uiProviders : {},
37185
37186     /**
37187     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37188     * child nodes before loading.
37189     */
37190     clearOnLoad : true,
37191
37192     /**
37193     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37194     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37195     * Grid query { data : [ .....] }
37196     */
37197     
37198     root : false,
37199      /**
37200     * @cfg {String} queryParam (optional) 
37201     * Name of the query as it will be passed on the querystring (defaults to 'node')
37202     * eg. the request will be ?node=[id]
37203     */
37204     
37205     
37206     queryParam: false,
37207     
37208     /**
37209      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37210      * This is called automatically when a node is expanded, but may be used to reload
37211      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37212      * @param {Roo.tree.TreeNode} node
37213      * @param {Function} callback
37214      */
37215     load : function(node, callback){
37216         if(this.clearOnLoad){
37217             while(node.firstChild){
37218                 node.removeChild(node.firstChild);
37219             }
37220         }
37221         if(node.attributes.children){ // preloaded json children
37222             var cs = node.attributes.children;
37223             for(var i = 0, len = cs.length; i < len; i++){
37224                 node.appendChild(this.createNode(cs[i]));
37225             }
37226             if(typeof callback == "function"){
37227                 callback();
37228             }
37229         }else if(this.dataUrl){
37230             this.requestData(node, callback);
37231         }
37232     },
37233
37234     getParams: function(node){
37235         var buf = [], bp = this.baseParams;
37236         for(var key in bp){
37237             if(typeof bp[key] != "function"){
37238                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37239             }
37240         }
37241         var n = this.queryParam === false ? 'node' : this.queryParam;
37242         buf.push(n + "=", encodeURIComponent(node.id));
37243         return buf.join("");
37244     },
37245
37246     requestData : function(node, callback){
37247         if(this.fireEvent("beforeload", this, node, callback) !== false){
37248             this.transId = Roo.Ajax.request({
37249                 method:this.requestMethod,
37250                 url: this.dataUrl||this.url,
37251                 success: this.handleResponse,
37252                 failure: this.handleFailure,
37253                 scope: this,
37254                 argument: {callback: callback, node: node},
37255                 params: this.getParams(node)
37256             });
37257         }else{
37258             // if the load is cancelled, make sure we notify
37259             // the node that we are done
37260             if(typeof callback == "function"){
37261                 callback();
37262             }
37263         }
37264     },
37265
37266     isLoading : function(){
37267         return this.transId ? true : false;
37268     },
37269
37270     abort : function(){
37271         if(this.isLoading()){
37272             Roo.Ajax.abort(this.transId);
37273         }
37274     },
37275
37276     // private
37277     createNode : function(attr)
37278     {
37279         // apply baseAttrs, nice idea Corey!
37280         if(this.baseAttrs){
37281             Roo.applyIf(attr, this.baseAttrs);
37282         }
37283         if(this.applyLoader !== false){
37284             attr.loader = this;
37285         }
37286         // uiProvider = depreciated..
37287         
37288         if(typeof(attr.uiProvider) == 'string'){
37289            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37290                 /**  eval:var:attr */ eval(attr.uiProvider);
37291         }
37292         if(typeof(this.uiProviders['default']) != 'undefined') {
37293             attr.uiProvider = this.uiProviders['default'];
37294         }
37295         
37296         this.fireEvent('create', this, attr);
37297         
37298         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37299         return(attr.leaf ?
37300                         new Roo.tree.TreeNode(attr) :
37301                         new Roo.tree.AsyncTreeNode(attr));
37302     },
37303
37304     processResponse : function(response, node, callback)
37305     {
37306         var json = response.responseText;
37307         try {
37308             
37309             var o = Roo.decode(json);
37310             
37311             if (this.root === false && typeof(o.success) != undefined) {
37312                 this.root = 'data'; // the default behaviour for list like data..
37313                 }
37314                 
37315             if (this.root !== false &&  !o.success) {
37316                 // it's a failure condition.
37317                 var a = response.argument;
37318                 this.fireEvent("loadexception", this, a.node, response);
37319                 Roo.log("Load failed - should have a handler really");
37320                 return;
37321             }
37322             
37323             
37324             
37325             if (this.root !== false) {
37326                  o = o[this.root];
37327             }
37328             
37329             for(var i = 0, len = o.length; i < len; i++){
37330                 var n = this.createNode(o[i]);
37331                 if(n){
37332                     node.appendChild(n);
37333                 }
37334             }
37335             if(typeof callback == "function"){
37336                 callback(this, node);
37337             }
37338         }catch(e){
37339             this.handleFailure(response);
37340         }
37341     },
37342
37343     handleResponse : function(response){
37344         this.transId = false;
37345         var a = response.argument;
37346         this.processResponse(response, a.node, a.callback);
37347         this.fireEvent("load", this, a.node, response);
37348     },
37349
37350     handleFailure : function(response)
37351     {
37352         // should handle failure better..
37353         this.transId = false;
37354         var a = response.argument;
37355         this.fireEvent("loadexception", this, a.node, response);
37356         if(typeof a.callback == "function"){
37357             a.callback(this, a.node);
37358         }
37359     }
37360 });/*
37361  * Based on:
37362  * Ext JS Library 1.1.1
37363  * Copyright(c) 2006-2007, Ext JS, LLC.
37364  *
37365  * Originally Released Under LGPL - original licence link has changed is not relivant.
37366  *
37367  * Fork - LGPL
37368  * <script type="text/javascript">
37369  */
37370
37371 /**
37372 * @class Roo.tree.TreeFilter
37373 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37374 * @param {TreePanel} tree
37375 * @param {Object} config (optional)
37376  */
37377 Roo.tree.TreeFilter = function(tree, config){
37378     this.tree = tree;
37379     this.filtered = {};
37380     Roo.apply(this, config);
37381 };
37382
37383 Roo.tree.TreeFilter.prototype = {
37384     clearBlank:false,
37385     reverse:false,
37386     autoClear:false,
37387     remove:false,
37388
37389      /**
37390      * Filter the data by a specific attribute.
37391      * @param {String/RegExp} value Either string that the attribute value
37392      * should start with or a RegExp to test against the attribute
37393      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37394      * @param {TreeNode} startNode (optional) The node to start the filter at.
37395      */
37396     filter : function(value, attr, startNode){
37397         attr = attr || "text";
37398         var f;
37399         if(typeof value == "string"){
37400             var vlen = value.length;
37401             // auto clear empty filter
37402             if(vlen == 0 && this.clearBlank){
37403                 this.clear();
37404                 return;
37405             }
37406             value = value.toLowerCase();
37407             f = function(n){
37408                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37409             };
37410         }else if(value.exec){ // regex?
37411             f = function(n){
37412                 return value.test(n.attributes[attr]);
37413             };
37414         }else{
37415             throw 'Illegal filter type, must be string or regex';
37416         }
37417         this.filterBy(f, null, startNode);
37418         },
37419
37420     /**
37421      * Filter by a function. The passed function will be called with each
37422      * node in the tree (or from the startNode). If the function returns true, the node is kept
37423      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37424      * @param {Function} fn The filter function
37425      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37426      */
37427     filterBy : function(fn, scope, startNode){
37428         startNode = startNode || this.tree.root;
37429         if(this.autoClear){
37430             this.clear();
37431         }
37432         var af = this.filtered, rv = this.reverse;
37433         var f = function(n){
37434             if(n == startNode){
37435                 return true;
37436             }
37437             if(af[n.id]){
37438                 return false;
37439             }
37440             var m = fn.call(scope || n, n);
37441             if(!m || rv){
37442                 af[n.id] = n;
37443                 n.ui.hide();
37444                 return false;
37445             }
37446             return true;
37447         };
37448         startNode.cascade(f);
37449         if(this.remove){
37450            for(var id in af){
37451                if(typeof id != "function"){
37452                    var n = af[id];
37453                    if(n && n.parentNode){
37454                        n.parentNode.removeChild(n);
37455                    }
37456                }
37457            }
37458         }
37459     },
37460
37461     /**
37462      * Clears the current filter. Note: with the "remove" option
37463      * set a filter cannot be cleared.
37464      */
37465     clear : function(){
37466         var t = this.tree;
37467         var af = this.filtered;
37468         for(var id in af){
37469             if(typeof id != "function"){
37470                 var n = af[id];
37471                 if(n){
37472                     n.ui.show();
37473                 }
37474             }
37475         }
37476         this.filtered = {};
37477     }
37478 };
37479 /*
37480  * Based on:
37481  * Ext JS Library 1.1.1
37482  * Copyright(c) 2006-2007, Ext JS, LLC.
37483  *
37484  * Originally Released Under LGPL - original licence link has changed is not relivant.
37485  *
37486  * Fork - LGPL
37487  * <script type="text/javascript">
37488  */
37489  
37490
37491 /**
37492  * @class Roo.tree.TreeSorter
37493  * Provides sorting of nodes in a TreePanel
37494  * 
37495  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37496  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37497  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37498  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37499  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37500  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37501  * @constructor
37502  * @param {TreePanel} tree
37503  * @param {Object} config
37504  */
37505 Roo.tree.TreeSorter = function(tree, config){
37506     Roo.apply(this, config);
37507     tree.on("beforechildrenrendered", this.doSort, this);
37508     tree.on("append", this.updateSort, this);
37509     tree.on("insert", this.updateSort, this);
37510     
37511     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37512     var p = this.property || "text";
37513     var sortType = this.sortType;
37514     var fs = this.folderSort;
37515     var cs = this.caseSensitive === true;
37516     var leafAttr = this.leafAttr || 'leaf';
37517
37518     this.sortFn = function(n1, n2){
37519         if(fs){
37520             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37521                 return 1;
37522             }
37523             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37524                 return -1;
37525             }
37526         }
37527         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37528         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37529         if(v1 < v2){
37530                         return dsc ? +1 : -1;
37531                 }else if(v1 > v2){
37532                         return dsc ? -1 : +1;
37533         }else{
37534                 return 0;
37535         }
37536     };
37537 };
37538
37539 Roo.tree.TreeSorter.prototype = {
37540     doSort : function(node){
37541         node.sort(this.sortFn);
37542     },
37543     
37544     compareNodes : function(n1, n2){
37545         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37546     },
37547     
37548     updateSort : function(tree, node){
37549         if(node.childrenRendered){
37550             this.doSort.defer(1, this, [node]);
37551         }
37552     }
37553 };/*
37554  * Based on:
37555  * Ext JS Library 1.1.1
37556  * Copyright(c) 2006-2007, Ext JS, LLC.
37557  *
37558  * Originally Released Under LGPL - original licence link has changed is not relivant.
37559  *
37560  * Fork - LGPL
37561  * <script type="text/javascript">
37562  */
37563
37564 if(Roo.dd.DropZone){
37565     
37566 Roo.tree.TreeDropZone = function(tree, config){
37567     this.allowParentInsert = false;
37568     this.allowContainerDrop = false;
37569     this.appendOnly = false;
37570     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37571     this.tree = tree;
37572     this.lastInsertClass = "x-tree-no-status";
37573     this.dragOverData = {};
37574 };
37575
37576 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37577     ddGroup : "TreeDD",
37578     scroll:  true,
37579     
37580     expandDelay : 1000,
37581     
37582     expandNode : function(node){
37583         if(node.hasChildNodes() && !node.isExpanded()){
37584             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37585         }
37586     },
37587     
37588     queueExpand : function(node){
37589         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37590     },
37591     
37592     cancelExpand : function(){
37593         if(this.expandProcId){
37594             clearTimeout(this.expandProcId);
37595             this.expandProcId = false;
37596         }
37597     },
37598     
37599     isValidDropPoint : function(n, pt, dd, e, data){
37600         if(!n || !data){ return false; }
37601         var targetNode = n.node;
37602         var dropNode = data.node;
37603         // default drop rules
37604         if(!(targetNode && targetNode.isTarget && pt)){
37605             return false;
37606         }
37607         if(pt == "append" && targetNode.allowChildren === false){
37608             return false;
37609         }
37610         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37611             return false;
37612         }
37613         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37614             return false;
37615         }
37616         // reuse the object
37617         var overEvent = this.dragOverData;
37618         overEvent.tree = this.tree;
37619         overEvent.target = targetNode;
37620         overEvent.data = data;
37621         overEvent.point = pt;
37622         overEvent.source = dd;
37623         overEvent.rawEvent = e;
37624         overEvent.dropNode = dropNode;
37625         overEvent.cancel = false;  
37626         var result = this.tree.fireEvent("nodedragover", overEvent);
37627         return overEvent.cancel === false && result !== false;
37628     },
37629     
37630     getDropPoint : function(e, n, dd)
37631     {
37632         var tn = n.node;
37633         if(tn.isRoot){
37634             return tn.allowChildren !== false ? "append" : false; // always append for root
37635         }
37636         var dragEl = n.ddel;
37637         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37638         var y = Roo.lib.Event.getPageY(e);
37639         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37640         
37641         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37642         var noAppend = tn.allowChildren === false;
37643         if(this.appendOnly || tn.parentNode.allowChildren === false){
37644             return noAppend ? false : "append";
37645         }
37646         var noBelow = false;
37647         if(!this.allowParentInsert){
37648             noBelow = tn.hasChildNodes() && tn.isExpanded();
37649         }
37650         var q = (b - t) / (noAppend ? 2 : 3);
37651         if(y >= t && y < (t + q)){
37652             return "above";
37653         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37654             return "below";
37655         }else{
37656             return "append";
37657         }
37658     },
37659     
37660     onNodeEnter : function(n, dd, e, data)
37661     {
37662         this.cancelExpand();
37663     },
37664     
37665     onNodeOver : function(n, dd, e, data)
37666     {
37667        
37668         var pt = this.getDropPoint(e, n, dd);
37669         var node = n.node;
37670         
37671         // auto node expand check
37672         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37673             this.queueExpand(node);
37674         }else if(pt != "append"){
37675             this.cancelExpand();
37676         }
37677         
37678         // set the insert point style on the target node
37679         var returnCls = this.dropNotAllowed;
37680         if(this.isValidDropPoint(n, pt, dd, e, data)){
37681            if(pt){
37682                var el = n.ddel;
37683                var cls;
37684                if(pt == "above"){
37685                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37686                    cls = "x-tree-drag-insert-above";
37687                }else if(pt == "below"){
37688                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37689                    cls = "x-tree-drag-insert-below";
37690                }else{
37691                    returnCls = "x-tree-drop-ok-append";
37692                    cls = "x-tree-drag-append";
37693                }
37694                if(this.lastInsertClass != cls){
37695                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37696                    this.lastInsertClass = cls;
37697                }
37698            }
37699        }
37700        return returnCls;
37701     },
37702     
37703     onNodeOut : function(n, dd, e, data){
37704         
37705         this.cancelExpand();
37706         this.removeDropIndicators(n);
37707     },
37708     
37709     onNodeDrop : function(n, dd, e, data){
37710         var point = this.getDropPoint(e, n, dd);
37711         var targetNode = n.node;
37712         targetNode.ui.startDrop();
37713         if(!this.isValidDropPoint(n, point, dd, e, data)){
37714             targetNode.ui.endDrop();
37715             return false;
37716         }
37717         // first try to find the drop node
37718         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37719         var dropEvent = {
37720             tree : this.tree,
37721             target: targetNode,
37722             data: data,
37723             point: point,
37724             source: dd,
37725             rawEvent: e,
37726             dropNode: dropNode,
37727             cancel: !dropNode   
37728         };
37729         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37730         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37731             targetNode.ui.endDrop();
37732             return false;
37733         }
37734         // allow target changing
37735         targetNode = dropEvent.target;
37736         if(point == "append" && !targetNode.isExpanded()){
37737             targetNode.expand(false, null, function(){
37738                 this.completeDrop(dropEvent);
37739             }.createDelegate(this));
37740         }else{
37741             this.completeDrop(dropEvent);
37742         }
37743         return true;
37744     },
37745     
37746     completeDrop : function(de){
37747         var ns = de.dropNode, p = de.point, t = de.target;
37748         if(!(ns instanceof Array)){
37749             ns = [ns];
37750         }
37751         var n;
37752         for(var i = 0, len = ns.length; i < len; i++){
37753             n = ns[i];
37754             if(p == "above"){
37755                 t.parentNode.insertBefore(n, t);
37756             }else if(p == "below"){
37757                 t.parentNode.insertBefore(n, t.nextSibling);
37758             }else{
37759                 t.appendChild(n);
37760             }
37761         }
37762         n.ui.focus();
37763         if(this.tree.hlDrop){
37764             n.ui.highlight();
37765         }
37766         t.ui.endDrop();
37767         this.tree.fireEvent("nodedrop", de);
37768     },
37769     
37770     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37771         if(this.tree.hlDrop){
37772             dropNode.ui.focus();
37773             dropNode.ui.highlight();
37774         }
37775         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37776     },
37777     
37778     getTree : function(){
37779         return this.tree;
37780     },
37781     
37782     removeDropIndicators : function(n){
37783         if(n && n.ddel){
37784             var el = n.ddel;
37785             Roo.fly(el).removeClass([
37786                     "x-tree-drag-insert-above",
37787                     "x-tree-drag-insert-below",
37788                     "x-tree-drag-append"]);
37789             this.lastInsertClass = "_noclass";
37790         }
37791     },
37792     
37793     beforeDragDrop : function(target, e, id){
37794         this.cancelExpand();
37795         return true;
37796     },
37797     
37798     afterRepair : function(data){
37799         if(data && Roo.enableFx){
37800             data.node.ui.highlight();
37801         }
37802         this.hideProxy();
37803     } 
37804     
37805 });
37806
37807 }
37808 /*
37809  * Based on:
37810  * Ext JS Library 1.1.1
37811  * Copyright(c) 2006-2007, Ext JS, LLC.
37812  *
37813  * Originally Released Under LGPL - original licence link has changed is not relivant.
37814  *
37815  * Fork - LGPL
37816  * <script type="text/javascript">
37817  */
37818  
37819
37820 if(Roo.dd.DragZone){
37821 Roo.tree.TreeDragZone = function(tree, config){
37822     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37823     this.tree = tree;
37824 };
37825
37826 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37827     ddGroup : "TreeDD",
37828    
37829     onBeforeDrag : function(data, e){
37830         var n = data.node;
37831         return n && n.draggable && !n.disabled;
37832     },
37833      
37834     
37835     onInitDrag : function(e){
37836         var data = this.dragData;
37837         this.tree.getSelectionModel().select(data.node);
37838         this.proxy.update("");
37839         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37840         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37841     },
37842     
37843     getRepairXY : function(e, data){
37844         return data.node.ui.getDDRepairXY();
37845     },
37846     
37847     onEndDrag : function(data, e){
37848         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37849         
37850         
37851     },
37852     
37853     onValidDrop : function(dd, e, id){
37854         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37855         this.hideProxy();
37856     },
37857     
37858     beforeInvalidDrop : function(e, id){
37859         // this scrolls the original position back into view
37860         var sm = this.tree.getSelectionModel();
37861         sm.clearSelections();
37862         sm.select(this.dragData.node);
37863     }
37864 });
37865 }/*
37866  * Based on:
37867  * Ext JS Library 1.1.1
37868  * Copyright(c) 2006-2007, Ext JS, LLC.
37869  *
37870  * Originally Released Under LGPL - original licence link has changed is not relivant.
37871  *
37872  * Fork - LGPL
37873  * <script type="text/javascript">
37874  */
37875 /**
37876  * @class Roo.tree.TreeEditor
37877  * @extends Roo.Editor
37878  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37879  * as the editor field.
37880  * @constructor
37881  * @param {Object} config (used to be the tree panel.)
37882  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37883  * 
37884  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37885  * @cfg {Roo.form.TextField} field [required] The field configuration
37886  *
37887  * 
37888  */
37889 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37890     var tree = config;
37891     var field;
37892     if (oldconfig) { // old style..
37893         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37894     } else {
37895         // new style..
37896         tree = config.tree;
37897         config.field = config.field  || {};
37898         config.field.xtype = 'TextField';
37899         field = Roo.factory(config.field, Roo.form);
37900     }
37901     config = config || {};
37902     
37903     
37904     this.addEvents({
37905         /**
37906          * @event beforenodeedit
37907          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37908          * false from the handler of this event.
37909          * @param {Editor} this
37910          * @param {Roo.tree.Node} node 
37911          */
37912         "beforenodeedit" : true
37913     });
37914     
37915     //Roo.log(config);
37916     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37917
37918     this.tree = tree;
37919
37920     tree.on('beforeclick', this.beforeNodeClick, this);
37921     tree.getTreeEl().on('mousedown', this.hide, this);
37922     this.on('complete', this.updateNode, this);
37923     this.on('beforestartedit', this.fitToTree, this);
37924     this.on('startedit', this.bindScroll, this, {delay:10});
37925     this.on('specialkey', this.onSpecialKey, this);
37926 };
37927
37928 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37929     /**
37930      * @cfg {String} alignment
37931      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37932      */
37933     alignment: "l-l",
37934     // inherit
37935     autoSize: false,
37936     /**
37937      * @cfg {Boolean} hideEl
37938      * True to hide the bound element while the editor is displayed (defaults to false)
37939      */
37940     hideEl : false,
37941     /**
37942      * @cfg {String} cls
37943      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37944      */
37945     cls: "x-small-editor x-tree-editor",
37946     /**
37947      * @cfg {Boolean} shim
37948      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37949      */
37950     shim:false,
37951     // inherit
37952     shadow:"frame",
37953     /**
37954      * @cfg {Number} maxWidth
37955      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37956      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37957      * scroll and client offsets into account prior to each edit.
37958      */
37959     maxWidth: 250,
37960
37961     editDelay : 350,
37962
37963     // private
37964     fitToTree : function(ed, el){
37965         var td = this.tree.getTreeEl().dom, nd = el.dom;
37966         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37967             td.scrollLeft = nd.offsetLeft;
37968         }
37969         var w = Math.min(
37970                 this.maxWidth,
37971                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37972         this.setSize(w, '');
37973         
37974         return this.fireEvent('beforenodeedit', this, this.editNode);
37975         
37976     },
37977
37978     // private
37979     triggerEdit : function(node){
37980         this.completeEdit();
37981         this.editNode = node;
37982         this.startEdit(node.ui.textNode, node.text);
37983     },
37984
37985     // private
37986     bindScroll : function(){
37987         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37988     },
37989
37990     // private
37991     beforeNodeClick : function(node, e){
37992         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37993         this.lastClick = new Date();
37994         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37995             e.stopEvent();
37996             this.triggerEdit(node);
37997             return false;
37998         }
37999         return true;
38000     },
38001
38002     // private
38003     updateNode : function(ed, value){
38004         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38005         this.editNode.setText(value);
38006     },
38007
38008     // private
38009     onHide : function(){
38010         Roo.tree.TreeEditor.superclass.onHide.call(this);
38011         if(this.editNode){
38012             this.editNode.ui.focus();
38013         }
38014     },
38015
38016     // private
38017     onSpecialKey : function(field, e){
38018         var k = e.getKey();
38019         if(k == e.ESC){
38020             e.stopEvent();
38021             this.cancelEdit();
38022         }else if(k == e.ENTER && !e.hasModifier()){
38023             e.stopEvent();
38024             this.completeEdit();
38025         }
38026     }
38027 });//<Script type="text/javascript">
38028 /*
38029  * Based on:
38030  * Ext JS Library 1.1.1
38031  * Copyright(c) 2006-2007, Ext JS, LLC.
38032  *
38033  * Originally Released Under LGPL - original licence link has changed is not relivant.
38034  *
38035  * Fork - LGPL
38036  * <script type="text/javascript">
38037  */
38038  
38039 /**
38040  * Not documented??? - probably should be...
38041  */
38042
38043 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38044     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38045     
38046     renderElements : function(n, a, targetNode, bulkRender){
38047         //consel.log("renderElements?");
38048         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38049
38050         var t = n.getOwnerTree();
38051         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38052         
38053         var cols = t.columns;
38054         var bw = t.borderWidth;
38055         var c = cols[0];
38056         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38057          var cb = typeof a.checked == "boolean";
38058         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38059         var colcls = 'x-t-' + tid + '-c0';
38060         var buf = [
38061             '<li class="x-tree-node">',
38062             
38063                 
38064                 '<div class="x-tree-node-el ', a.cls,'">',
38065                     // extran...
38066                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38067                 
38068                 
38069                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38070                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38071                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38072                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38073                            (a.iconCls ? ' '+a.iconCls : ''),
38074                            '" unselectable="on" />',
38075                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38076                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38077                              
38078                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38079                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38080                             '<span unselectable="on" qtip="' + tx + '">',
38081                              tx,
38082                              '</span></a>' ,
38083                     '</div>',
38084                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38085                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38086                  ];
38087         for(var i = 1, len = cols.length; i < len; i++){
38088             c = cols[i];
38089             colcls = 'x-t-' + tid + '-c' +i;
38090             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38091             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38092                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38093                       "</div>");
38094          }
38095          
38096          buf.push(
38097             '</a>',
38098             '<div class="x-clear"></div></div>',
38099             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38100             "</li>");
38101         
38102         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38103             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38104                                 n.nextSibling.ui.getEl(), buf.join(""));
38105         }else{
38106             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38107         }
38108         var el = this.wrap.firstChild;
38109         this.elRow = el;
38110         this.elNode = el.firstChild;
38111         this.ranchor = el.childNodes[1];
38112         this.ctNode = this.wrap.childNodes[1];
38113         var cs = el.firstChild.childNodes;
38114         this.indentNode = cs[0];
38115         this.ecNode = cs[1];
38116         this.iconNode = cs[2];
38117         var index = 3;
38118         if(cb){
38119             this.checkbox = cs[3];
38120             index++;
38121         }
38122         this.anchor = cs[index];
38123         
38124         this.textNode = cs[index].firstChild;
38125         
38126         //el.on("click", this.onClick, this);
38127         //el.on("dblclick", this.onDblClick, this);
38128         
38129         
38130        // console.log(this);
38131     },
38132     initEvents : function(){
38133         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38134         
38135             
38136         var a = this.ranchor;
38137
38138         var el = Roo.get(a);
38139
38140         if(Roo.isOpera){ // opera render bug ignores the CSS
38141             el.setStyle("text-decoration", "none");
38142         }
38143
38144         el.on("click", this.onClick, this);
38145         el.on("dblclick", this.onDblClick, this);
38146         el.on("contextmenu", this.onContextMenu, this);
38147         
38148     },
38149     
38150     /*onSelectedChange : function(state){
38151         if(state){
38152             this.focus();
38153             this.addClass("x-tree-selected");
38154         }else{
38155             //this.blur();
38156             this.removeClass("x-tree-selected");
38157         }
38158     },*/
38159     addClass : function(cls){
38160         if(this.elRow){
38161             Roo.fly(this.elRow).addClass(cls);
38162         }
38163         
38164     },
38165     
38166     
38167     removeClass : function(cls){
38168         if(this.elRow){
38169             Roo.fly(this.elRow).removeClass(cls);
38170         }
38171     }
38172
38173     
38174     
38175 });//<Script type="text/javascript">
38176
38177 /*
38178  * Based on:
38179  * Ext JS Library 1.1.1
38180  * Copyright(c) 2006-2007, Ext JS, LLC.
38181  *
38182  * Originally Released Under LGPL - original licence link has changed is not relivant.
38183  *
38184  * Fork - LGPL
38185  * <script type="text/javascript">
38186  */
38187  
38188
38189 /**
38190  * @class Roo.tree.ColumnTree
38191  * @extends Roo.tree.TreePanel
38192  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38193  * @cfg {int} borderWidth  compined right/left border allowance
38194  * @constructor
38195  * @param {String/HTMLElement/Element} el The container element
38196  * @param {Object} config
38197  */
38198 Roo.tree.ColumnTree =  function(el, config)
38199 {
38200    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38201    this.addEvents({
38202         /**
38203         * @event resize
38204         * Fire this event on a container when it resizes
38205         * @param {int} w Width
38206         * @param {int} h Height
38207         */
38208        "resize" : true
38209     });
38210     this.on('resize', this.onResize, this);
38211 };
38212
38213 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38214     //lines:false,
38215     
38216     
38217     borderWidth: Roo.isBorderBox ? 0 : 2, 
38218     headEls : false,
38219     
38220     render : function(){
38221         // add the header.....
38222        
38223         Roo.tree.ColumnTree.superclass.render.apply(this);
38224         
38225         this.el.addClass('x-column-tree');
38226         
38227         this.headers = this.el.createChild(
38228             {cls:'x-tree-headers'},this.innerCt.dom);
38229    
38230         var cols = this.columns, c;
38231         var totalWidth = 0;
38232         this.headEls = [];
38233         var  len = cols.length;
38234         for(var i = 0; i < len; i++){
38235              c = cols[i];
38236              totalWidth += c.width;
38237             this.headEls.push(this.headers.createChild({
38238                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38239                  cn: {
38240                      cls:'x-tree-hd-text',
38241                      html: c.header
38242                  },
38243                  style:'width:'+(c.width-this.borderWidth)+'px;'
38244              }));
38245         }
38246         this.headers.createChild({cls:'x-clear'});
38247         // prevent floats from wrapping when clipped
38248         this.headers.setWidth(totalWidth);
38249         //this.innerCt.setWidth(totalWidth);
38250         this.innerCt.setStyle({ overflow: 'auto' });
38251         this.onResize(this.width, this.height);
38252              
38253         
38254     },
38255     onResize : function(w,h)
38256     {
38257         this.height = h;
38258         this.width = w;
38259         // resize cols..
38260         this.innerCt.setWidth(this.width);
38261         this.innerCt.setHeight(this.height-20);
38262         
38263         // headers...
38264         var cols = this.columns, c;
38265         var totalWidth = 0;
38266         var expEl = false;
38267         var len = cols.length;
38268         for(var i = 0; i < len; i++){
38269             c = cols[i];
38270             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38271                 // it's the expander..
38272                 expEl  = this.headEls[i];
38273                 continue;
38274             }
38275             totalWidth += c.width;
38276             
38277         }
38278         if (expEl) {
38279             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38280         }
38281         this.headers.setWidth(w-20);
38282
38283         
38284         
38285         
38286     }
38287 });
38288 /*
38289  * Based on:
38290  * Ext JS Library 1.1.1
38291  * Copyright(c) 2006-2007, Ext JS, LLC.
38292  *
38293  * Originally Released Under LGPL - original licence link has changed is not relivant.
38294  *
38295  * Fork - LGPL
38296  * <script type="text/javascript">
38297  */
38298  
38299 /**
38300  * @class Roo.menu.Menu
38301  * @extends Roo.util.Observable
38302  * @children Roo.menu.BaseItem
38303  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38304  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38305  * @constructor
38306  * Creates a new Menu
38307  * @param {Object} config Configuration options
38308  */
38309 Roo.menu.Menu = function(config){
38310     
38311     Roo.menu.Menu.superclass.constructor.call(this, config);
38312     
38313     this.id = this.id || Roo.id();
38314     this.addEvents({
38315         /**
38316          * @event beforeshow
38317          * Fires before this menu is displayed
38318          * @param {Roo.menu.Menu} this
38319          */
38320         beforeshow : true,
38321         /**
38322          * @event beforehide
38323          * Fires before this menu is hidden
38324          * @param {Roo.menu.Menu} this
38325          */
38326         beforehide : true,
38327         /**
38328          * @event show
38329          * Fires after this menu is displayed
38330          * @param {Roo.menu.Menu} this
38331          */
38332         show : true,
38333         /**
38334          * @event hide
38335          * Fires after this menu is hidden
38336          * @param {Roo.menu.Menu} this
38337          */
38338         hide : true,
38339         /**
38340          * @event click
38341          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38342          * @param {Roo.menu.Menu} this
38343          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38344          * @param {Roo.EventObject} e
38345          */
38346         click : true,
38347         /**
38348          * @event mouseover
38349          * Fires when the mouse is hovering over this menu
38350          * @param {Roo.menu.Menu} this
38351          * @param {Roo.EventObject} e
38352          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38353          */
38354         mouseover : true,
38355         /**
38356          * @event mouseout
38357          * Fires when the mouse exits this menu
38358          * @param {Roo.menu.Menu} this
38359          * @param {Roo.EventObject} e
38360          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38361          */
38362         mouseout : true,
38363         /**
38364          * @event itemclick
38365          * Fires when a menu item contained in this menu is clicked
38366          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38367          * @param {Roo.EventObject} e
38368          */
38369         itemclick: true
38370     });
38371     if (this.registerMenu) {
38372         Roo.menu.MenuMgr.register(this);
38373     }
38374     
38375     var mis = this.items;
38376     this.items = new Roo.util.MixedCollection();
38377     if(mis){
38378         this.add.apply(this, mis);
38379     }
38380 };
38381
38382 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38383     /**
38384      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38385      */
38386     minWidth : 120,
38387     /**
38388      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38389      * for bottom-right shadow (defaults to "sides")
38390      */
38391     shadow : "sides",
38392     /**
38393      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38394      * this menu (defaults to "tl-tr?")
38395      */
38396     subMenuAlign : "tl-tr?",
38397     /**
38398      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38399      * relative to its element of origin (defaults to "tl-bl?")
38400      */
38401     defaultAlign : "tl-bl?",
38402     /**
38403      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38404      */
38405     allowOtherMenus : false,
38406     /**
38407      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38408      */
38409     registerMenu : true,
38410
38411     hidden:true,
38412
38413     // private
38414     render : function(){
38415         if(this.el){
38416             return;
38417         }
38418         var el = this.el = new Roo.Layer({
38419             cls: "x-menu",
38420             shadow:this.shadow,
38421             constrain: false,
38422             parentEl: this.parentEl || document.body,
38423             zindex:15000
38424         });
38425
38426         this.keyNav = new Roo.menu.MenuNav(this);
38427
38428         if(this.plain){
38429             el.addClass("x-menu-plain");
38430         }
38431         if(this.cls){
38432             el.addClass(this.cls);
38433         }
38434         // generic focus element
38435         this.focusEl = el.createChild({
38436             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38437         });
38438         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38439         //disabling touch- as it's causing issues ..
38440         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38441         ul.on('click'   , this.onClick, this);
38442         
38443         
38444         ul.on("mouseover", this.onMouseOver, this);
38445         ul.on("mouseout", this.onMouseOut, this);
38446         this.items.each(function(item){
38447             if (item.hidden) {
38448                 return;
38449             }
38450             
38451             var li = document.createElement("li");
38452             li.className = "x-menu-list-item";
38453             ul.dom.appendChild(li);
38454             item.render(li, this);
38455         }, this);
38456         this.ul = ul;
38457         this.autoWidth();
38458     },
38459
38460     // private
38461     autoWidth : function(){
38462         var el = this.el, ul = this.ul;
38463         if(!el){
38464             return;
38465         }
38466         var w = this.width;
38467         if(w){
38468             el.setWidth(w);
38469         }else if(Roo.isIE){
38470             el.setWidth(this.minWidth);
38471             var t = el.dom.offsetWidth; // force recalc
38472             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38473         }
38474     },
38475
38476     // private
38477     delayAutoWidth : function(){
38478         if(this.rendered){
38479             if(!this.awTask){
38480                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38481             }
38482             this.awTask.delay(20);
38483         }
38484     },
38485
38486     // private
38487     findTargetItem : function(e){
38488         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38489         if(t && t.menuItemId){
38490             return this.items.get(t.menuItemId);
38491         }
38492     },
38493
38494     // private
38495     onClick : function(e){
38496         Roo.log("menu.onClick");
38497         var t = this.findTargetItem(e);
38498         if(!t){
38499             return;
38500         }
38501         Roo.log(e);
38502         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38503             if(t == this.activeItem && t.shouldDeactivate(e)){
38504                 this.activeItem.deactivate();
38505                 delete this.activeItem;
38506                 return;
38507             }
38508             if(t.canActivate){
38509                 this.setActiveItem(t, true);
38510             }
38511             return;
38512             
38513             
38514         }
38515         
38516         t.onClick(e);
38517         this.fireEvent("click", this, t, e);
38518     },
38519
38520     // private
38521     setActiveItem : function(item, autoExpand){
38522         if(item != this.activeItem){
38523             if(this.activeItem){
38524                 this.activeItem.deactivate();
38525             }
38526             this.activeItem = item;
38527             item.activate(autoExpand);
38528         }else if(autoExpand){
38529             item.expandMenu();
38530         }
38531     },
38532
38533     // private
38534     tryActivate : function(start, step){
38535         var items = this.items;
38536         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38537             var item = items.get(i);
38538             if(!item.disabled && item.canActivate){
38539                 this.setActiveItem(item, false);
38540                 return item;
38541             }
38542         }
38543         return false;
38544     },
38545
38546     // private
38547     onMouseOver : function(e){
38548         var t;
38549         if(t = this.findTargetItem(e)){
38550             if(t.canActivate && !t.disabled){
38551                 this.setActiveItem(t, true);
38552             }
38553         }
38554         this.fireEvent("mouseover", this, e, t);
38555     },
38556
38557     // private
38558     onMouseOut : function(e){
38559         var t;
38560         if(t = this.findTargetItem(e)){
38561             if(t == this.activeItem && t.shouldDeactivate(e)){
38562                 this.activeItem.deactivate();
38563                 delete this.activeItem;
38564             }
38565         }
38566         this.fireEvent("mouseout", this, e, t);
38567     },
38568
38569     /**
38570      * Read-only.  Returns true if the menu is currently displayed, else false.
38571      * @type Boolean
38572      */
38573     isVisible : function(){
38574         return this.el && !this.hidden;
38575     },
38576
38577     /**
38578      * Displays this menu relative to another element
38579      * @param {String/HTMLElement/Roo.Element} element The element to align to
38580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38581      * the element (defaults to this.defaultAlign)
38582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38583      */
38584     show : function(el, pos, parentMenu){
38585         this.parentMenu = parentMenu;
38586         if(!this.el){
38587             this.render();
38588         }
38589         this.fireEvent("beforeshow", this);
38590         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38591     },
38592
38593     /**
38594      * Displays this menu at a specific xy position
38595      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38596      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38597      */
38598     showAt : function(xy, parentMenu, /* private: */_e){
38599         this.parentMenu = parentMenu;
38600         if(!this.el){
38601             this.render();
38602         }
38603         if(_e !== false){
38604             this.fireEvent("beforeshow", this);
38605             xy = this.el.adjustForConstraints(xy);
38606         }
38607         this.el.setXY(xy);
38608         this.el.show();
38609         this.hidden = false;
38610         this.focus();
38611         this.fireEvent("show", this);
38612     },
38613
38614     focus : function(){
38615         if(!this.hidden){
38616             this.doFocus.defer(50, this);
38617         }
38618     },
38619
38620     doFocus : function(){
38621         if(!this.hidden){
38622             this.focusEl.focus();
38623         }
38624     },
38625
38626     /**
38627      * Hides this menu and optionally all parent menus
38628      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38629      */
38630     hide : function(deep){
38631         if(this.el && this.isVisible()){
38632             this.fireEvent("beforehide", this);
38633             if(this.activeItem){
38634                 this.activeItem.deactivate();
38635                 this.activeItem = null;
38636             }
38637             this.el.hide();
38638             this.hidden = true;
38639             this.fireEvent("hide", this);
38640         }
38641         if(deep === true && this.parentMenu){
38642             this.parentMenu.hide(true);
38643         }
38644     },
38645
38646     /**
38647      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38648      * Any of the following are valid:
38649      * <ul>
38650      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38651      * <li>An HTMLElement object which will be converted to a menu item</li>
38652      * <li>A menu item config object that will be created as a new menu item</li>
38653      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38654      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38655      * </ul>
38656      * Usage:
38657      * <pre><code>
38658 // Create the menu
38659 var menu = new Roo.menu.Menu();
38660
38661 // Create a menu item to add by reference
38662 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38663
38664 // Add a bunch of items at once using different methods.
38665 // Only the last item added will be returned.
38666 var item = menu.add(
38667     menuItem,                // add existing item by ref
38668     'Dynamic Item',          // new TextItem
38669     '-',                     // new separator
38670     { text: 'Config Item' }  // new item by config
38671 );
38672 </code></pre>
38673      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38674      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38675      */
38676     add : function(){
38677         var a = arguments, l = a.length, item;
38678         for(var i = 0; i < l; i++){
38679             var el = a[i];
38680             if ((typeof(el) == "object") && el.xtype && el.xns) {
38681                 el = Roo.factory(el, Roo.menu);
38682             }
38683             
38684             if(el.render){ // some kind of Item
38685                 item = this.addItem(el);
38686             }else if(typeof el == "string"){ // string
38687                 if(el == "separator" || el == "-"){
38688                     item = this.addSeparator();
38689                 }else{
38690                     item = this.addText(el);
38691                 }
38692             }else if(el.tagName || el.el){ // element
38693                 item = this.addElement(el);
38694             }else if(typeof el == "object"){ // must be menu item config?
38695                 item = this.addMenuItem(el);
38696             }
38697         }
38698         return item;
38699     },
38700
38701     /**
38702      * Returns this menu's underlying {@link Roo.Element} object
38703      * @return {Roo.Element} The element
38704      */
38705     getEl : function(){
38706         if(!this.el){
38707             this.render();
38708         }
38709         return this.el;
38710     },
38711
38712     /**
38713      * Adds a separator bar to the menu
38714      * @return {Roo.menu.Item} The menu item that was added
38715      */
38716     addSeparator : function(){
38717         return this.addItem(new Roo.menu.Separator());
38718     },
38719
38720     /**
38721      * Adds an {@link Roo.Element} object to the menu
38722      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38723      * @return {Roo.menu.Item} The menu item that was added
38724      */
38725     addElement : function(el){
38726         return this.addItem(new Roo.menu.BaseItem(el));
38727     },
38728
38729     /**
38730      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38731      * @param {Roo.menu.Item} item The menu item to add
38732      * @return {Roo.menu.Item} The menu item that was added
38733      */
38734     addItem : function(item){
38735         this.items.add(item);
38736         if(this.ul){
38737             var li = document.createElement("li");
38738             li.className = "x-menu-list-item";
38739             this.ul.dom.appendChild(li);
38740             item.render(li, this);
38741             this.delayAutoWidth();
38742         }
38743         return item;
38744     },
38745
38746     /**
38747      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38748      * @param {Object} config A MenuItem config object
38749      * @return {Roo.menu.Item} The menu item that was added
38750      */
38751     addMenuItem : function(config){
38752         if(!(config instanceof Roo.menu.Item)){
38753             if(typeof config.checked == "boolean"){ // must be check menu item config?
38754                 config = new Roo.menu.CheckItem(config);
38755             }else{
38756                 config = new Roo.menu.Item(config);
38757             }
38758         }
38759         return this.addItem(config);
38760     },
38761
38762     /**
38763      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38764      * @param {String} text The text to display in the menu item
38765      * @return {Roo.menu.Item} The menu item that was added
38766      */
38767     addText : function(text){
38768         return this.addItem(new Roo.menu.TextItem({ text : text }));
38769     },
38770
38771     /**
38772      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38773      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38774      * @param {Roo.menu.Item} item The menu item to add
38775      * @return {Roo.menu.Item} The menu item that was added
38776      */
38777     insert : function(index, item){
38778         this.items.insert(index, item);
38779         if(this.ul){
38780             var li = document.createElement("li");
38781             li.className = "x-menu-list-item";
38782             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38783             item.render(li, this);
38784             this.delayAutoWidth();
38785         }
38786         return item;
38787     },
38788
38789     /**
38790      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38791      * @param {Roo.menu.Item} item The menu item to remove
38792      */
38793     remove : function(item){
38794         this.items.removeKey(item.id);
38795         item.destroy();
38796     },
38797
38798     /**
38799      * Removes and destroys all items in the menu
38800      */
38801     removeAll : function(){
38802         var f;
38803         while(f = this.items.first()){
38804             this.remove(f);
38805         }
38806     }
38807 });
38808
38809 // MenuNav is a private utility class used internally by the Menu
38810 Roo.menu.MenuNav = function(menu){
38811     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38812     this.scope = this.menu = menu;
38813 };
38814
38815 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38816     doRelay : function(e, h){
38817         var k = e.getKey();
38818         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38819             this.menu.tryActivate(0, 1);
38820             return false;
38821         }
38822         return h.call(this.scope || this, e, this.menu);
38823     },
38824
38825     up : function(e, m){
38826         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38827             m.tryActivate(m.items.length-1, -1);
38828         }
38829     },
38830
38831     down : function(e, m){
38832         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38833             m.tryActivate(0, 1);
38834         }
38835     },
38836
38837     right : function(e, m){
38838         if(m.activeItem){
38839             m.activeItem.expandMenu(true);
38840         }
38841     },
38842
38843     left : function(e, m){
38844         m.hide();
38845         if(m.parentMenu && m.parentMenu.activeItem){
38846             m.parentMenu.activeItem.activate();
38847         }
38848     },
38849
38850     enter : function(e, m){
38851         if(m.activeItem){
38852             e.stopPropagation();
38853             m.activeItem.onClick(e);
38854             m.fireEvent("click", this, m.activeItem);
38855             return true;
38856         }
38857     }
38858 });/*
38859  * Based on:
38860  * Ext JS Library 1.1.1
38861  * Copyright(c) 2006-2007, Ext JS, LLC.
38862  *
38863  * Originally Released Under LGPL - original licence link has changed is not relivant.
38864  *
38865  * Fork - LGPL
38866  * <script type="text/javascript">
38867  */
38868  
38869 /**
38870  * @class Roo.menu.MenuMgr
38871  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38872  * @static
38873  */
38874 Roo.menu.MenuMgr = function(){
38875    var menus, active, groups = {}, attached = false, lastShow = new Date();
38876
38877    // private - called when first menu is created
38878    function init(){
38879        menus = {};
38880        active = new Roo.util.MixedCollection();
38881        Roo.get(document).addKeyListener(27, function(){
38882            if(active.length > 0){
38883                hideAll();
38884            }
38885        });
38886    }
38887
38888    // private
38889    function hideAll(){
38890        if(active && active.length > 0){
38891            var c = active.clone();
38892            c.each(function(m){
38893                m.hide();
38894            });
38895        }
38896    }
38897
38898    // private
38899    function onHide(m){
38900        active.remove(m);
38901        if(active.length < 1){
38902            Roo.get(document).un("mousedown", onMouseDown);
38903            attached = false;
38904        }
38905    }
38906
38907    // private
38908    function onShow(m){
38909        var last = active.last();
38910        lastShow = new Date();
38911        active.add(m);
38912        if(!attached){
38913            Roo.get(document).on("mousedown", onMouseDown);
38914            attached = true;
38915        }
38916        if(m.parentMenu){
38917           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38918           m.parentMenu.activeChild = m;
38919        }else if(last && last.isVisible()){
38920           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38921        }
38922    }
38923
38924    // private
38925    function onBeforeHide(m){
38926        if(m.activeChild){
38927            m.activeChild.hide();
38928        }
38929        if(m.autoHideTimer){
38930            clearTimeout(m.autoHideTimer);
38931            delete m.autoHideTimer;
38932        }
38933    }
38934
38935    // private
38936    function onBeforeShow(m){
38937        var pm = m.parentMenu;
38938        if(!pm && !m.allowOtherMenus){
38939            hideAll();
38940        }else if(pm && pm.activeChild && active != m){
38941            pm.activeChild.hide();
38942        }
38943    }
38944
38945    // private
38946    function onMouseDown(e){
38947        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38948            hideAll();
38949        }
38950    }
38951
38952    // private
38953    function onBeforeCheck(mi, state){
38954        if(state){
38955            var g = groups[mi.group];
38956            for(var i = 0, l = g.length; i < l; i++){
38957                if(g[i] != mi){
38958                    g[i].setChecked(false);
38959                }
38960            }
38961        }
38962    }
38963
38964    return {
38965
38966        /**
38967         * Hides all menus that are currently visible
38968         */
38969        hideAll : function(){
38970             hideAll();  
38971        },
38972
38973        // private
38974        register : function(menu){
38975            if(!menus){
38976                init();
38977            }
38978            menus[menu.id] = menu;
38979            menu.on("beforehide", onBeforeHide);
38980            menu.on("hide", onHide);
38981            menu.on("beforeshow", onBeforeShow);
38982            menu.on("show", onShow);
38983            var g = menu.group;
38984            if(g && menu.events["checkchange"]){
38985                if(!groups[g]){
38986                    groups[g] = [];
38987                }
38988                groups[g].push(menu);
38989                menu.on("checkchange", onCheck);
38990            }
38991        },
38992
38993         /**
38994          * Returns a {@link Roo.menu.Menu} object
38995          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38996          * be used to generate and return a new Menu instance.
38997          */
38998        get : function(menu){
38999            if(typeof menu == "string"){ // menu id
39000                return menus[menu];
39001            }else if(menu.events){  // menu instance
39002                return menu;
39003            }else if(typeof menu.length == 'number'){ // array of menu items?
39004                return new Roo.menu.Menu({items:menu});
39005            }else{ // otherwise, must be a config
39006                return new Roo.menu.Menu(menu);
39007            }
39008        },
39009
39010        // private
39011        unregister : function(menu){
39012            delete menus[menu.id];
39013            menu.un("beforehide", onBeforeHide);
39014            menu.un("hide", onHide);
39015            menu.un("beforeshow", onBeforeShow);
39016            menu.un("show", onShow);
39017            var g = menu.group;
39018            if(g && menu.events["checkchange"]){
39019                groups[g].remove(menu);
39020                menu.un("checkchange", onCheck);
39021            }
39022        },
39023
39024        // private
39025        registerCheckable : function(menuItem){
39026            var g = menuItem.group;
39027            if(g){
39028                if(!groups[g]){
39029                    groups[g] = [];
39030                }
39031                groups[g].push(menuItem);
39032                menuItem.on("beforecheckchange", onBeforeCheck);
39033            }
39034        },
39035
39036        // private
39037        unregisterCheckable : function(menuItem){
39038            var g = menuItem.group;
39039            if(g){
39040                groups[g].remove(menuItem);
39041                menuItem.un("beforecheckchange", onBeforeCheck);
39042            }
39043        }
39044    };
39045 }();/*
39046  * Based on:
39047  * Ext JS Library 1.1.1
39048  * Copyright(c) 2006-2007, Ext JS, LLC.
39049  *
39050  * Originally Released Under LGPL - original licence link has changed is not relivant.
39051  *
39052  * Fork - LGPL
39053  * <script type="text/javascript">
39054  */
39055  
39056
39057 /**
39058  * @class Roo.menu.BaseItem
39059  * @extends Roo.Component
39060  * @abstract
39061  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39062  * management and base configuration options shared by all menu components.
39063  * @constructor
39064  * Creates a new BaseItem
39065  * @param {Object} config Configuration options
39066  */
39067 Roo.menu.BaseItem = function(config){
39068     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39069
39070     this.addEvents({
39071         /**
39072          * @event click
39073          * Fires when this item is clicked
39074          * @param {Roo.menu.BaseItem} this
39075          * @param {Roo.EventObject} e
39076          */
39077         click: true,
39078         /**
39079          * @event activate
39080          * Fires when this item is activated
39081          * @param {Roo.menu.BaseItem} this
39082          */
39083         activate : true,
39084         /**
39085          * @event deactivate
39086          * Fires when this item is deactivated
39087          * @param {Roo.menu.BaseItem} this
39088          */
39089         deactivate : true
39090     });
39091
39092     if(this.handler){
39093         this.on("click", this.handler, this.scope, true);
39094     }
39095 };
39096
39097 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39098     /**
39099      * @cfg {Function} handler
39100      * A function that will handle the click event of this menu item (defaults to undefined)
39101      */
39102     /**
39103      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39104      */
39105     canActivate : false,
39106     
39107      /**
39108      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39109      */
39110     hidden: false,
39111     
39112     /**
39113      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39114      */
39115     activeClass : "x-menu-item-active",
39116     /**
39117      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39118      */
39119     hideOnClick : true,
39120     /**
39121      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39122      */
39123     hideDelay : 100,
39124
39125     // private
39126     ctype: "Roo.menu.BaseItem",
39127
39128     // private
39129     actionMode : "container",
39130
39131     // private
39132     render : function(container, parentMenu){
39133         this.parentMenu = parentMenu;
39134         Roo.menu.BaseItem.superclass.render.call(this, container);
39135         this.container.menuItemId = this.id;
39136     },
39137
39138     // private
39139     onRender : function(container, position){
39140         this.el = Roo.get(this.el);
39141         container.dom.appendChild(this.el.dom);
39142     },
39143
39144     // private
39145     onClick : function(e){
39146         if(!this.disabled && this.fireEvent("click", this, e) !== false
39147                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39148             this.handleClick(e);
39149         }else{
39150             e.stopEvent();
39151         }
39152     },
39153
39154     // private
39155     activate : function(){
39156         if(this.disabled){
39157             return false;
39158         }
39159         var li = this.container;
39160         li.addClass(this.activeClass);
39161         this.region = li.getRegion().adjust(2, 2, -2, -2);
39162         this.fireEvent("activate", this);
39163         return true;
39164     },
39165
39166     // private
39167     deactivate : function(){
39168         this.container.removeClass(this.activeClass);
39169         this.fireEvent("deactivate", this);
39170     },
39171
39172     // private
39173     shouldDeactivate : function(e){
39174         return !this.region || !this.region.contains(e.getPoint());
39175     },
39176
39177     // private
39178     handleClick : function(e){
39179         if(this.hideOnClick){
39180             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39181         }
39182     },
39183
39184     // private
39185     expandMenu : function(autoActivate){
39186         // do nothing
39187     },
39188
39189     // private
39190     hideMenu : function(){
39191         // do nothing
39192     }
39193 });/*
39194  * Based on:
39195  * Ext JS Library 1.1.1
39196  * Copyright(c) 2006-2007, Ext JS, LLC.
39197  *
39198  * Originally Released Under LGPL - original licence link has changed is not relivant.
39199  *
39200  * Fork - LGPL
39201  * <script type="text/javascript">
39202  */
39203  
39204 /**
39205  * @class Roo.menu.Adapter
39206  * @extends Roo.menu.BaseItem
39207  * @abstract
39208  * 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.
39209  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39210  * @constructor
39211  * Creates a new Adapter
39212  * @param {Object} config Configuration options
39213  */
39214 Roo.menu.Adapter = function(component, config){
39215     Roo.menu.Adapter.superclass.constructor.call(this, config);
39216     this.component = component;
39217 };
39218 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39219     // private
39220     canActivate : true,
39221
39222     // private
39223     onRender : function(container, position){
39224         this.component.render(container);
39225         this.el = this.component.getEl();
39226     },
39227
39228     // private
39229     activate : function(){
39230         if(this.disabled){
39231             return false;
39232         }
39233         this.component.focus();
39234         this.fireEvent("activate", this);
39235         return true;
39236     },
39237
39238     // private
39239     deactivate : function(){
39240         this.fireEvent("deactivate", this);
39241     },
39242
39243     // private
39244     disable : function(){
39245         this.component.disable();
39246         Roo.menu.Adapter.superclass.disable.call(this);
39247     },
39248
39249     // private
39250     enable : function(){
39251         this.component.enable();
39252         Roo.menu.Adapter.superclass.enable.call(this);
39253     }
39254 });/*
39255  * Based on:
39256  * Ext JS Library 1.1.1
39257  * Copyright(c) 2006-2007, Ext JS, LLC.
39258  *
39259  * Originally Released Under LGPL - original licence link has changed is not relivant.
39260  *
39261  * Fork - LGPL
39262  * <script type="text/javascript">
39263  */
39264
39265 /**
39266  * @class Roo.menu.TextItem
39267  * @extends Roo.menu.BaseItem
39268  * Adds a static text string to a menu, usually used as either a heading or group separator.
39269  * Note: old style constructor with text is still supported.
39270  * 
39271  * @constructor
39272  * Creates a new TextItem
39273  * @param {Object} cfg Configuration
39274  */
39275 Roo.menu.TextItem = function(cfg){
39276     if (typeof(cfg) == 'string') {
39277         this.text = cfg;
39278     } else {
39279         Roo.apply(this,cfg);
39280     }
39281     
39282     Roo.menu.TextItem.superclass.constructor.call(this);
39283 };
39284
39285 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39286     /**
39287      * @cfg {String} text Text to show on item.
39288      */
39289     text : '',
39290     
39291     /**
39292      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39293      */
39294     hideOnClick : false,
39295     /**
39296      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39297      */
39298     itemCls : "x-menu-text",
39299
39300     // private
39301     onRender : function(){
39302         var s = document.createElement("span");
39303         s.className = this.itemCls;
39304         s.innerHTML = this.text;
39305         this.el = s;
39306         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39307     }
39308 });/*
39309  * Based on:
39310  * Ext JS Library 1.1.1
39311  * Copyright(c) 2006-2007, Ext JS, LLC.
39312  *
39313  * Originally Released Under LGPL - original licence link has changed is not relivant.
39314  *
39315  * Fork - LGPL
39316  * <script type="text/javascript">
39317  */
39318
39319 /**
39320  * @class Roo.menu.Separator
39321  * @extends Roo.menu.BaseItem
39322  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39323  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39324  * @constructor
39325  * @param {Object} config Configuration options
39326  */
39327 Roo.menu.Separator = function(config){
39328     Roo.menu.Separator.superclass.constructor.call(this, config);
39329 };
39330
39331 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39332     /**
39333      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39334      */
39335     itemCls : "x-menu-sep",
39336     /**
39337      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39338      */
39339     hideOnClick : false,
39340
39341     // private
39342     onRender : function(li){
39343         var s = document.createElement("span");
39344         s.className = this.itemCls;
39345         s.innerHTML = "&#160;";
39346         this.el = s;
39347         li.addClass("x-menu-sep-li");
39348         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39349     }
39350 });/*
39351  * Based on:
39352  * Ext JS Library 1.1.1
39353  * Copyright(c) 2006-2007, Ext JS, LLC.
39354  *
39355  * Originally Released Under LGPL - original licence link has changed is not relivant.
39356  *
39357  * Fork - LGPL
39358  * <script type="text/javascript">
39359  */
39360 /**
39361  * @class Roo.menu.Item
39362  * @extends Roo.menu.BaseItem
39363  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39364  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39365  * activation and click handling.
39366  * @constructor
39367  * Creates a new Item
39368  * @param {Object} config Configuration options
39369  */
39370 Roo.menu.Item = function(config){
39371     Roo.menu.Item.superclass.constructor.call(this, config);
39372     if(this.menu){
39373         this.menu = Roo.menu.MenuMgr.get(this.menu);
39374     }
39375 };
39376 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39377     /**
39378      * @cfg {Roo.menu.Menu} menu
39379      * A Sub menu
39380      */
39381     /**
39382      * @cfg {String} text
39383      * The text to show on the menu item.
39384      */
39385     text: '',
39386      /**
39387      * @cfg {String} HTML to render in menu
39388      * The text to show on the menu item (HTML version).
39389      */
39390     html: '',
39391     /**
39392      * @cfg {String} icon
39393      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39394      */
39395     icon: undefined,
39396     /**
39397      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39398      */
39399     itemCls : "x-menu-item",
39400     /**
39401      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39402      */
39403     canActivate : true,
39404     /**
39405      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39406      */
39407     showDelay: 200,
39408     // doc'd in BaseItem
39409     hideDelay: 200,
39410
39411     // private
39412     ctype: "Roo.menu.Item",
39413     
39414     // private
39415     onRender : function(container, position){
39416         var el = document.createElement("a");
39417         el.hideFocus = true;
39418         el.unselectable = "on";
39419         el.href = this.href || "#";
39420         if(this.hrefTarget){
39421             el.target = this.hrefTarget;
39422         }
39423         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39424         
39425         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39426         
39427         el.innerHTML = String.format(
39428                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39429                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39430         this.el = el;
39431         Roo.menu.Item.superclass.onRender.call(this, container, position);
39432     },
39433
39434     /**
39435      * Sets the text to display in this menu item
39436      * @param {String} text The text to display
39437      * @param {Boolean} isHTML true to indicate text is pure html.
39438      */
39439     setText : function(text, isHTML){
39440         if (isHTML) {
39441             this.html = text;
39442         } else {
39443             this.text = text;
39444             this.html = '';
39445         }
39446         if(this.rendered){
39447             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39448      
39449             this.el.update(String.format(
39450                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39451                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39452             this.parentMenu.autoWidth();
39453         }
39454     },
39455
39456     // private
39457     handleClick : function(e){
39458         if(!this.href){ // if no link defined, stop the event automatically
39459             e.stopEvent();
39460         }
39461         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39462     },
39463
39464     // private
39465     activate : function(autoExpand){
39466         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39467             this.focus();
39468             if(autoExpand){
39469                 this.expandMenu();
39470             }
39471         }
39472         return true;
39473     },
39474
39475     // private
39476     shouldDeactivate : function(e){
39477         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39478             if(this.menu && this.menu.isVisible()){
39479                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39480             }
39481             return true;
39482         }
39483         return false;
39484     },
39485
39486     // private
39487     deactivate : function(){
39488         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39489         this.hideMenu();
39490     },
39491
39492     // private
39493     expandMenu : function(autoActivate){
39494         if(!this.disabled && this.menu){
39495             clearTimeout(this.hideTimer);
39496             delete this.hideTimer;
39497             if(!this.menu.isVisible() && !this.showTimer){
39498                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39499             }else if (this.menu.isVisible() && autoActivate){
39500                 this.menu.tryActivate(0, 1);
39501             }
39502         }
39503     },
39504
39505     // private
39506     deferExpand : function(autoActivate){
39507         delete this.showTimer;
39508         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39509         if(autoActivate){
39510             this.menu.tryActivate(0, 1);
39511         }
39512     },
39513
39514     // private
39515     hideMenu : function(){
39516         clearTimeout(this.showTimer);
39517         delete this.showTimer;
39518         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39519             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39520         }
39521     },
39522
39523     // private
39524     deferHide : function(){
39525         delete this.hideTimer;
39526         this.menu.hide();
39527     }
39528 });/*
39529  * Based on:
39530  * Ext JS Library 1.1.1
39531  * Copyright(c) 2006-2007, Ext JS, LLC.
39532  *
39533  * Originally Released Under LGPL - original licence link has changed is not relivant.
39534  *
39535  * Fork - LGPL
39536  * <script type="text/javascript">
39537  */
39538  
39539 /**
39540  * @class Roo.menu.CheckItem
39541  * @extends Roo.menu.Item
39542  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39543  * @constructor
39544  * Creates a new CheckItem
39545  * @param {Object} config Configuration options
39546  */
39547 Roo.menu.CheckItem = function(config){
39548     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39549     this.addEvents({
39550         /**
39551          * @event beforecheckchange
39552          * Fires before the checked value is set, providing an opportunity to cancel if needed
39553          * @param {Roo.menu.CheckItem} this
39554          * @param {Boolean} checked The new checked value that will be set
39555          */
39556         "beforecheckchange" : true,
39557         /**
39558          * @event checkchange
39559          * Fires after the checked value has been set
39560          * @param {Roo.menu.CheckItem} this
39561          * @param {Boolean} checked The checked value that was set
39562          */
39563         "checkchange" : true
39564     });
39565     if(this.checkHandler){
39566         this.on('checkchange', this.checkHandler, this.scope);
39567     }
39568 };
39569 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39570     /**
39571      * @cfg {String} group
39572      * All check items with the same group name will automatically be grouped into a single-select
39573      * radio button group (defaults to '')
39574      */
39575     /**
39576      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39577      */
39578     itemCls : "x-menu-item x-menu-check-item",
39579     /**
39580      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39581      */
39582     groupClass : "x-menu-group-item",
39583
39584     /**
39585      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39586      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39587      * initialized with checked = true will be rendered as checked.
39588      */
39589     checked: false,
39590
39591     // private
39592     ctype: "Roo.menu.CheckItem",
39593
39594     // private
39595     onRender : function(c){
39596         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39597         if(this.group){
39598             this.el.addClass(this.groupClass);
39599         }
39600         Roo.menu.MenuMgr.registerCheckable(this);
39601         if(this.checked){
39602             this.checked = false;
39603             this.setChecked(true, true);
39604         }
39605     },
39606
39607     // private
39608     destroy : function(){
39609         if(this.rendered){
39610             Roo.menu.MenuMgr.unregisterCheckable(this);
39611         }
39612         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39613     },
39614
39615     /**
39616      * Set the checked state of this item
39617      * @param {Boolean} checked The new checked value
39618      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39619      */
39620     setChecked : function(state, suppressEvent){
39621         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39622             if(this.container){
39623                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39624             }
39625             this.checked = state;
39626             if(suppressEvent !== true){
39627                 this.fireEvent("checkchange", this, state);
39628             }
39629         }
39630     },
39631
39632     // private
39633     handleClick : function(e){
39634        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39635            this.setChecked(!this.checked);
39636        }
39637        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39638     }
39639 });/*
39640  * Based on:
39641  * Ext JS Library 1.1.1
39642  * Copyright(c) 2006-2007, Ext JS, LLC.
39643  *
39644  * Originally Released Under LGPL - original licence link has changed is not relivant.
39645  *
39646  * Fork - LGPL
39647  * <script type="text/javascript">
39648  */
39649  
39650 /**
39651  * @class Roo.menu.DateItem
39652  * @extends Roo.menu.Adapter
39653  * A menu item that wraps the {@link Roo.DatPicker} component.
39654  * @constructor
39655  * Creates a new DateItem
39656  * @param {Object} config Configuration options
39657  */
39658 Roo.menu.DateItem = function(config){
39659     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39660     /** The Roo.DatePicker object @type Roo.DatePicker */
39661     this.picker = this.component;
39662     this.addEvents({select: true});
39663     
39664     this.picker.on("render", function(picker){
39665         picker.getEl().swallowEvent("click");
39666         picker.container.addClass("x-menu-date-item");
39667     });
39668
39669     this.picker.on("select", this.onSelect, this);
39670 };
39671
39672 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39673     // private
39674     onSelect : function(picker, date){
39675         this.fireEvent("select", this, date, picker);
39676         Roo.menu.DateItem.superclass.handleClick.call(this);
39677     }
39678 });/*
39679  * Based on:
39680  * Ext JS Library 1.1.1
39681  * Copyright(c) 2006-2007, Ext JS, LLC.
39682  *
39683  * Originally Released Under LGPL - original licence link has changed is not relivant.
39684  *
39685  * Fork - LGPL
39686  * <script type="text/javascript">
39687  */
39688  
39689 /**
39690  * @class Roo.menu.ColorItem
39691  * @extends Roo.menu.Adapter
39692  * A menu item that wraps the {@link Roo.ColorPalette} component.
39693  * @constructor
39694  * Creates a new ColorItem
39695  * @param {Object} config Configuration options
39696  */
39697 Roo.menu.ColorItem = function(config){
39698     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39699     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39700     this.palette = this.component;
39701     this.relayEvents(this.palette, ["select"]);
39702     if(this.selectHandler){
39703         this.on('select', this.selectHandler, this.scope);
39704     }
39705 };
39706 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39707  * Based on:
39708  * Ext JS Library 1.1.1
39709  * Copyright(c) 2006-2007, Ext JS, LLC.
39710  *
39711  * Originally Released Under LGPL - original licence link has changed is not relivant.
39712  *
39713  * Fork - LGPL
39714  * <script type="text/javascript">
39715  */
39716  
39717
39718 /**
39719  * @class Roo.menu.DateMenu
39720  * @extends Roo.menu.Menu
39721  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39722  * @constructor
39723  * Creates a new DateMenu
39724  * @param {Object} config Configuration options
39725  */
39726 Roo.menu.DateMenu = function(config){
39727     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39728     this.plain = true;
39729     var di = new Roo.menu.DateItem(config);
39730     this.add(di);
39731     /**
39732      * The {@link Roo.DatePicker} instance for this DateMenu
39733      * @type DatePicker
39734      */
39735     this.picker = di.picker;
39736     /**
39737      * @event select
39738      * @param {DatePicker} picker
39739      * @param {Date} date
39740      */
39741     this.relayEvents(di, ["select"]);
39742     this.on('beforeshow', function(){
39743         if(this.picker){
39744             this.picker.hideMonthPicker(false);
39745         }
39746     }, this);
39747 };
39748 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39749     cls:'x-date-menu'
39750 });/*
39751  * Based on:
39752  * Ext JS Library 1.1.1
39753  * Copyright(c) 2006-2007, Ext JS, LLC.
39754  *
39755  * Originally Released Under LGPL - original licence link has changed is not relivant.
39756  *
39757  * Fork - LGPL
39758  * <script type="text/javascript">
39759  */
39760  
39761
39762 /**
39763  * @class Roo.menu.ColorMenu
39764  * @extends Roo.menu.Menu
39765  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39766  * @constructor
39767  * Creates a new ColorMenu
39768  * @param {Object} config Configuration options
39769  */
39770 Roo.menu.ColorMenu = function(config){
39771     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39772     this.plain = true;
39773     var ci = new Roo.menu.ColorItem(config);
39774     this.add(ci);
39775     /**
39776      * The {@link Roo.ColorPalette} instance for this ColorMenu
39777      * @type ColorPalette
39778      */
39779     this.palette = ci.palette;
39780     /**
39781      * @event select
39782      * @param {ColorPalette} palette
39783      * @param {String} color
39784      */
39785     this.relayEvents(ci, ["select"]);
39786 };
39787 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39788  * Based on:
39789  * Ext JS Library 1.1.1
39790  * Copyright(c) 2006-2007, Ext JS, LLC.
39791  *
39792  * Originally Released Under LGPL - original licence link has changed is not relivant.
39793  *
39794  * Fork - LGPL
39795  * <script type="text/javascript">
39796  */
39797  
39798 /**
39799  * @class Roo.form.TextItem
39800  * @extends Roo.BoxComponent
39801  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39802  * @constructor
39803  * Creates a new TextItem
39804  * @param {Object} config Configuration options
39805  */
39806 Roo.form.TextItem = function(config){
39807     Roo.form.TextItem.superclass.constructor.call(this, config);
39808 };
39809
39810 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39811     
39812     /**
39813      * @cfg {String} tag the tag for this item (default div)
39814      */
39815     tag : 'div',
39816     /**
39817      * @cfg {String} html the content for this item
39818      */
39819     html : '',
39820     
39821     getAutoCreate : function()
39822     {
39823         var cfg = {
39824             id: this.id,
39825             tag: this.tag,
39826             html: this.html,
39827             cls: 'x-form-item'
39828         };
39829         
39830         return cfg;
39831         
39832     },
39833     
39834     onRender : function(ct, position)
39835     {
39836         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39837         
39838         if(!this.el){
39839             var cfg = this.getAutoCreate();
39840             if(!cfg.name){
39841                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39842             }
39843             if (!cfg.name.length) {
39844                 delete cfg.name;
39845             }
39846             this.el = ct.createChild(cfg, position);
39847         }
39848     },
39849     /*
39850      * setHTML
39851      * @param {String} html update the Contents of the element.
39852      */
39853     setHTML : function(html)
39854     {
39855         this.fieldEl.dom.innerHTML = html;
39856     }
39857     
39858 });/*
39859  * Based on:
39860  * Ext JS Library 1.1.1
39861  * Copyright(c) 2006-2007, Ext JS, LLC.
39862  *
39863  * Originally Released Under LGPL - original licence link has changed is not relivant.
39864  *
39865  * Fork - LGPL
39866  * <script type="text/javascript">
39867  */
39868  
39869 /**
39870  * @class Roo.form.Field
39871  * @extends Roo.BoxComponent
39872  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39873  * @constructor
39874  * Creates a new Field
39875  * @param {Object} config Configuration options
39876  */
39877 Roo.form.Field = function(config){
39878     Roo.form.Field.superclass.constructor.call(this, config);
39879 };
39880
39881 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39882     /**
39883      * @cfg {String} fieldLabel Label to use when rendering a form.
39884      */
39885        /**
39886      * @cfg {String} qtip Mouse over tip
39887      */
39888      
39889     /**
39890      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39891      */
39892     invalidClass : "x-form-invalid",
39893     /**
39894      * @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")
39895      */
39896     invalidText : "The value in this field is invalid",
39897     /**
39898      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39899      */
39900     focusClass : "x-form-focus",
39901     /**
39902      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39903       automatic validation (defaults to "keyup").
39904      */
39905     validationEvent : "keyup",
39906     /**
39907      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39908      */
39909     validateOnBlur : true,
39910     /**
39911      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39912      */
39913     validationDelay : 250,
39914     /**
39915      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39916      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39917      */
39918     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39919     /**
39920      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39921      */
39922     fieldClass : "x-form-field",
39923     /**
39924      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39925      *<pre>
39926 Value         Description
39927 -----------   ----------------------------------------------------------------------
39928 qtip          Display a quick tip when the user hovers over the field
39929 title         Display a default browser title attribute popup
39930 under         Add a block div beneath the field containing the error text
39931 side          Add an error icon to the right of the field with a popup on hover
39932 [element id]  Add the error text directly to the innerHTML of the specified element
39933 </pre>
39934      */
39935     msgTarget : 'qtip',
39936     /**
39937      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39938      */
39939     msgFx : 'normal',
39940
39941     /**
39942      * @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.
39943      */
39944     readOnly : false,
39945
39946     /**
39947      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39948      */
39949     disabled : false,
39950
39951     /**
39952      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39953      */
39954     inputType : undefined,
39955     
39956     /**
39957      * @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).
39958          */
39959         tabIndex : undefined,
39960         
39961     // private
39962     isFormField : true,
39963
39964     // private
39965     hasFocus : false,
39966     /**
39967      * @property {Roo.Element} fieldEl
39968      * Element Containing the rendered Field (with label etc.)
39969      */
39970     /**
39971      * @cfg {Mixed} value A value to initialize this field with.
39972      */
39973     value : undefined,
39974
39975     /**
39976      * @cfg {String} name The field's HTML name attribute.
39977      */
39978     /**
39979      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39980      */
39981     // private
39982     loadedValue : false,
39983      
39984      
39985         // private ??
39986         initComponent : function(){
39987         Roo.form.Field.superclass.initComponent.call(this);
39988         this.addEvents({
39989             /**
39990              * @event focus
39991              * Fires when this field receives input focus.
39992              * @param {Roo.form.Field} this
39993              */
39994             focus : true,
39995             /**
39996              * @event blur
39997              * Fires when this field loses input focus.
39998              * @param {Roo.form.Field} this
39999              */
40000             blur : true,
40001             /**
40002              * @event specialkey
40003              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40004              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40005              * @param {Roo.form.Field} this
40006              * @param {Roo.EventObject} e The event object
40007              */
40008             specialkey : true,
40009             /**
40010              * @event change
40011              * Fires just before the field blurs if the field value has changed.
40012              * @param {Roo.form.Field} this
40013              * @param {Mixed} newValue The new value
40014              * @param {Mixed} oldValue The original value
40015              */
40016             change : true,
40017             /**
40018              * @event invalid
40019              * Fires after the field has been marked as invalid.
40020              * @param {Roo.form.Field} this
40021              * @param {String} msg The validation message
40022              */
40023             invalid : true,
40024             /**
40025              * @event valid
40026              * Fires after the field has been validated with no errors.
40027              * @param {Roo.form.Field} this
40028              */
40029             valid : true,
40030              /**
40031              * @event keyup
40032              * Fires after the key up
40033              * @param {Roo.form.Field} this
40034              * @param {Roo.EventObject}  e The event Object
40035              */
40036             keyup : true
40037         });
40038     },
40039
40040     /**
40041      * Returns the name attribute of the field if available
40042      * @return {String} name The field name
40043      */
40044     getName: function(){
40045          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40046     },
40047
40048     // private
40049     onRender : function(ct, position){
40050         Roo.form.Field.superclass.onRender.call(this, ct, position);
40051         if(!this.el){
40052             var cfg = this.getAutoCreate();
40053             if(!cfg.name){
40054                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40055             }
40056             if (!cfg.name.length) {
40057                 delete cfg.name;
40058             }
40059             if(this.inputType){
40060                 cfg.type = this.inputType;
40061             }
40062             this.el = ct.createChild(cfg, position);
40063         }
40064         var type = this.el.dom.type;
40065         if(type){
40066             if(type == 'password'){
40067                 type = 'text';
40068             }
40069             this.el.addClass('x-form-'+type);
40070         }
40071         if(this.readOnly){
40072             this.el.dom.readOnly = true;
40073         }
40074         if(this.tabIndex !== undefined){
40075             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40076         }
40077
40078         this.el.addClass([this.fieldClass, this.cls]);
40079         this.initValue();
40080     },
40081
40082     /**
40083      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40084      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40085      * @return {Roo.form.Field} this
40086      */
40087     applyTo : function(target){
40088         this.allowDomMove = false;
40089         this.el = Roo.get(target);
40090         this.render(this.el.dom.parentNode);
40091         return this;
40092     },
40093
40094     // private
40095     initValue : function(){
40096         if(this.value !== undefined){
40097             this.setValue(this.value);
40098         }else if(this.el.dom.value.length > 0){
40099             this.setValue(this.el.dom.value);
40100         }
40101     },
40102
40103     /**
40104      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40105      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40106      */
40107     isDirty : function() {
40108         if(this.disabled) {
40109             return false;
40110         }
40111         return String(this.getValue()) !== String(this.originalValue);
40112     },
40113
40114     /**
40115      * stores the current value in loadedValue
40116      */
40117     resetHasChanged : function()
40118     {
40119         this.loadedValue = String(this.getValue());
40120     },
40121     /**
40122      * checks the current value against the 'loaded' value.
40123      * Note - will return false if 'resetHasChanged' has not been called first.
40124      */
40125     hasChanged : function()
40126     {
40127         if(this.disabled || this.readOnly) {
40128             return false;
40129         }
40130         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40131     },
40132     
40133     
40134     
40135     // private
40136     afterRender : function(){
40137         Roo.form.Field.superclass.afterRender.call(this);
40138         this.initEvents();
40139     },
40140
40141     // private
40142     fireKey : function(e){
40143         //Roo.log('field ' + e.getKey());
40144         if(e.isNavKeyPress()){
40145             this.fireEvent("specialkey", this, e);
40146         }
40147     },
40148
40149     /**
40150      * Resets the current field value to the originally loaded value and clears any validation messages
40151      */
40152     reset : function(){
40153         this.setValue(this.resetValue);
40154         this.originalValue = this.getValue();
40155         this.clearInvalid();
40156     },
40157
40158     // private
40159     initEvents : function(){
40160         // safari killled keypress - so keydown is now used..
40161         this.el.on("keydown" , this.fireKey,  this);
40162         this.el.on("focus", this.onFocus,  this);
40163         this.el.on("blur", this.onBlur,  this);
40164         this.el.relayEvent('keyup', this);
40165
40166         // reference to original value for reset
40167         this.originalValue = this.getValue();
40168         this.resetValue =  this.getValue();
40169     },
40170
40171     // private
40172     onFocus : function(){
40173         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40174             this.el.addClass(this.focusClass);
40175         }
40176         if(!this.hasFocus){
40177             this.hasFocus = true;
40178             this.startValue = this.getValue();
40179             this.fireEvent("focus", this);
40180         }
40181     },
40182
40183     beforeBlur : Roo.emptyFn,
40184
40185     // private
40186     onBlur : function(){
40187         this.beforeBlur();
40188         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40189             this.el.removeClass(this.focusClass);
40190         }
40191         this.hasFocus = false;
40192         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40193             this.validate();
40194         }
40195         var v = this.getValue();
40196         if(String(v) !== String(this.startValue)){
40197             this.fireEvent('change', this, v, this.startValue);
40198         }
40199         this.fireEvent("blur", this);
40200     },
40201
40202     /**
40203      * Returns whether or not the field value is currently valid
40204      * @param {Boolean} preventMark True to disable marking the field invalid
40205      * @return {Boolean} True if the value is valid, else false
40206      */
40207     isValid : function(preventMark){
40208         if(this.disabled){
40209             return true;
40210         }
40211         var restore = this.preventMark;
40212         this.preventMark = preventMark === true;
40213         var v = this.validateValue(this.processValue(this.getRawValue()));
40214         this.preventMark = restore;
40215         return v;
40216     },
40217
40218     /**
40219      * Validates the field value
40220      * @return {Boolean} True if the value is valid, else false
40221      */
40222     validate : function(){
40223         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40224             this.clearInvalid();
40225             return true;
40226         }
40227         return false;
40228     },
40229
40230     processValue : function(value){
40231         return value;
40232     },
40233
40234     // private
40235     // Subclasses should provide the validation implementation by overriding this
40236     validateValue : function(value){
40237         return true;
40238     },
40239
40240     /**
40241      * Mark this field as invalid
40242      * @param {String} msg The validation message
40243      */
40244     markInvalid : function(msg){
40245         if(!this.rendered || this.preventMark){ // not rendered
40246             return;
40247         }
40248         
40249         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40250         
40251         obj.el.addClass(this.invalidClass);
40252         msg = msg || this.invalidText;
40253         switch(this.msgTarget){
40254             case 'qtip':
40255                 obj.el.dom.qtip = msg;
40256                 obj.el.dom.qclass = 'x-form-invalid-tip';
40257                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40258                     Roo.QuickTips.enable();
40259                 }
40260                 break;
40261             case 'title':
40262                 this.el.dom.title = msg;
40263                 break;
40264             case 'under':
40265                 if(!this.errorEl){
40266                     var elp = this.el.findParent('.x-form-element', 5, true);
40267                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40268                     this.errorEl.setWidth(elp.getWidth(true)-20);
40269                 }
40270                 this.errorEl.update(msg);
40271                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40272                 break;
40273             case 'side':
40274                 if(!this.errorIcon){
40275                     var elp = this.el.findParent('.x-form-element', 5, true);
40276                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40277                 }
40278                 this.alignErrorIcon();
40279                 this.errorIcon.dom.qtip = msg;
40280                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40281                 this.errorIcon.show();
40282                 this.on('resize', this.alignErrorIcon, this);
40283                 break;
40284             default:
40285                 var t = Roo.getDom(this.msgTarget);
40286                 t.innerHTML = msg;
40287                 t.style.display = this.msgDisplay;
40288                 break;
40289         }
40290         this.fireEvent('invalid', this, msg);
40291     },
40292
40293     // private
40294     alignErrorIcon : function(){
40295         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40296     },
40297
40298     /**
40299      * Clear any invalid styles/messages for this field
40300      */
40301     clearInvalid : function(){
40302         if(!this.rendered || this.preventMark){ // not rendered
40303             return;
40304         }
40305         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40306         
40307         obj.el.removeClass(this.invalidClass);
40308         switch(this.msgTarget){
40309             case 'qtip':
40310                 obj.el.dom.qtip = '';
40311                 break;
40312             case 'title':
40313                 this.el.dom.title = '';
40314                 break;
40315             case 'under':
40316                 if(this.errorEl){
40317                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40318                 }
40319                 break;
40320             case 'side':
40321                 if(this.errorIcon){
40322                     this.errorIcon.dom.qtip = '';
40323                     this.errorIcon.hide();
40324                     this.un('resize', this.alignErrorIcon, this);
40325                 }
40326                 break;
40327             default:
40328                 var t = Roo.getDom(this.msgTarget);
40329                 t.innerHTML = '';
40330                 t.style.display = 'none';
40331                 break;
40332         }
40333         this.fireEvent('valid', this);
40334     },
40335
40336     /**
40337      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40338      * @return {Mixed} value The field value
40339      */
40340     getRawValue : function(){
40341         var v = this.el.getValue();
40342         
40343         return v;
40344     },
40345
40346     /**
40347      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40348      * @return {Mixed} value The field value
40349      */
40350     getValue : function(){
40351         var v = this.el.getValue();
40352          
40353         return v;
40354     },
40355
40356     /**
40357      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40358      * @param {Mixed} value The value to set
40359      */
40360     setRawValue : function(v){
40361         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40362     },
40363
40364     /**
40365      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40366      * @param {Mixed} value The value to set
40367      */
40368     setValue : function(v){
40369         this.value = v;
40370         if(this.rendered){
40371             this.el.dom.value = (v === null || v === undefined ? '' : v);
40372              this.validate();
40373         }
40374     },
40375
40376     adjustSize : function(w, h){
40377         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40378         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40379         return s;
40380     },
40381
40382     adjustWidth : function(tag, w){
40383         tag = tag.toLowerCase();
40384         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40385             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40386                 if(tag == 'input'){
40387                     return w + 2;
40388                 }
40389                 if(tag == 'textarea'){
40390                     return w-2;
40391                 }
40392             }else if(Roo.isOpera){
40393                 if(tag == 'input'){
40394                     return w + 2;
40395                 }
40396                 if(tag == 'textarea'){
40397                     return w-2;
40398                 }
40399             }
40400         }
40401         return w;
40402     }
40403 });
40404
40405
40406 // anything other than normal should be considered experimental
40407 Roo.form.Field.msgFx = {
40408     normal : {
40409         show: function(msgEl, f){
40410             msgEl.setDisplayed('block');
40411         },
40412
40413         hide : function(msgEl, f){
40414             msgEl.setDisplayed(false).update('');
40415         }
40416     },
40417
40418     slide : {
40419         show: function(msgEl, f){
40420             msgEl.slideIn('t', {stopFx:true});
40421         },
40422
40423         hide : function(msgEl, f){
40424             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40425         }
40426     },
40427
40428     slideRight : {
40429         show: function(msgEl, f){
40430             msgEl.fixDisplay();
40431             msgEl.alignTo(f.el, 'tl-tr');
40432             msgEl.slideIn('l', {stopFx:true});
40433         },
40434
40435         hide : function(msgEl, f){
40436             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40437         }
40438     }
40439 };/*
40440  * Based on:
40441  * Ext JS Library 1.1.1
40442  * Copyright(c) 2006-2007, Ext JS, LLC.
40443  *
40444  * Originally Released Under LGPL - original licence link has changed is not relivant.
40445  *
40446  * Fork - LGPL
40447  * <script type="text/javascript">
40448  */
40449  
40450
40451 /**
40452  * @class Roo.form.TextField
40453  * @extends Roo.form.Field
40454  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40455  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40456  * @constructor
40457  * Creates a new TextField
40458  * @param {Object} config Configuration options
40459  */
40460 Roo.form.TextField = function(config){
40461     Roo.form.TextField.superclass.constructor.call(this, config);
40462     this.addEvents({
40463         /**
40464          * @event autosize
40465          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40466          * according to the default logic, but this event provides a hook for the developer to apply additional
40467          * logic at runtime to resize the field if needed.
40468              * @param {Roo.form.Field} this This text field
40469              * @param {Number} width The new field width
40470              */
40471         autosize : true
40472     });
40473 };
40474
40475 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40476     /**
40477      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40478      */
40479     grow : false,
40480     /**
40481      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40482      */
40483     growMin : 30,
40484     /**
40485      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40486      */
40487     growMax : 800,
40488     /**
40489      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40490      */
40491     vtype : null,
40492     /**
40493      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40494      */
40495     maskRe : null,
40496     /**
40497      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40498      */
40499     disableKeyFilter : false,
40500     /**
40501      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40502      */
40503     allowBlank : true,
40504     /**
40505      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40506      */
40507     minLength : 0,
40508     /**
40509      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40510      */
40511     maxLength : Number.MAX_VALUE,
40512     /**
40513      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40514      */
40515     minLengthText : "The minimum length for this field is {0}",
40516     /**
40517      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40518      */
40519     maxLengthText : "The maximum length for this field is {0}",
40520     /**
40521      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40522      */
40523     selectOnFocus : false,
40524     /**
40525      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40526      */    
40527     allowLeadingSpace : false,
40528     /**
40529      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40530      */
40531     blankText : "This field is required",
40532     /**
40533      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40534      * If available, this function will be called only after the basic validators all return true, and will be passed the
40535      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40536      */
40537     validator : null,
40538     /**
40539      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40540      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40541      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40542      */
40543     regex : null,
40544     /**
40545      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40546      */
40547     regexText : "",
40548     /**
40549      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40550      */
40551     emptyText : null,
40552    
40553
40554     // private
40555     initEvents : function()
40556     {
40557         if (this.emptyText) {
40558             this.el.attr('placeholder', this.emptyText);
40559         }
40560         
40561         Roo.form.TextField.superclass.initEvents.call(this);
40562         if(this.validationEvent == 'keyup'){
40563             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40564             this.el.on('keyup', this.filterValidation, this);
40565         }
40566         else if(this.validationEvent !== false){
40567             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40568         }
40569         
40570         if(this.selectOnFocus){
40571             this.on("focus", this.preFocus, this);
40572         }
40573         if (!this.allowLeadingSpace) {
40574             this.on('blur', this.cleanLeadingSpace, this);
40575         }
40576         
40577         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40578             this.el.on("keypress", this.filterKeys, this);
40579         }
40580         if(this.grow){
40581             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40582             this.el.on("click", this.autoSize,  this);
40583         }
40584         if(this.el.is('input[type=password]') && Roo.isSafari){
40585             this.el.on('keydown', this.SafariOnKeyDown, this);
40586         }
40587     },
40588
40589     processValue : function(value){
40590         if(this.stripCharsRe){
40591             var newValue = value.replace(this.stripCharsRe, '');
40592             if(newValue !== value){
40593                 this.setRawValue(newValue);
40594                 return newValue;
40595             }
40596         }
40597         return value;
40598     },
40599
40600     filterValidation : function(e){
40601         if(!e.isNavKeyPress()){
40602             this.validationTask.delay(this.validationDelay);
40603         }
40604     },
40605
40606     // private
40607     onKeyUp : function(e){
40608         if(!e.isNavKeyPress()){
40609             this.autoSize();
40610         }
40611     },
40612     // private - clean the leading white space
40613     cleanLeadingSpace : function(e)
40614     {
40615         if ( this.inputType == 'file') {
40616             return;
40617         }
40618         
40619         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40620     },
40621     /**
40622      * Resets the current field value to the originally-loaded value and clears any validation messages.
40623      *  
40624      */
40625     reset : function(){
40626         Roo.form.TextField.superclass.reset.call(this);
40627        
40628     }, 
40629     // private
40630     preFocus : function(){
40631         
40632         if(this.selectOnFocus){
40633             this.el.dom.select();
40634         }
40635     },
40636
40637     
40638     // private
40639     filterKeys : function(e){
40640         var k = e.getKey();
40641         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40642             return;
40643         }
40644         var c = e.getCharCode(), cc = String.fromCharCode(c);
40645         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40646             return;
40647         }
40648         if(!this.maskRe.test(cc)){
40649             e.stopEvent();
40650         }
40651     },
40652
40653     setValue : function(v){
40654         
40655         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40656         
40657         this.autoSize();
40658     },
40659
40660     /**
40661      * Validates a value according to the field's validation rules and marks the field as invalid
40662      * if the validation fails
40663      * @param {Mixed} value The value to validate
40664      * @return {Boolean} True if the value is valid, else false
40665      */
40666     validateValue : function(value){
40667         if(value.length < 1)  { // if it's blank
40668              if(this.allowBlank){
40669                 this.clearInvalid();
40670                 return true;
40671              }else{
40672                 this.markInvalid(this.blankText);
40673                 return false;
40674              }
40675         }
40676         if(value.length < this.minLength){
40677             this.markInvalid(String.format(this.minLengthText, this.minLength));
40678             return false;
40679         }
40680         if(value.length > this.maxLength){
40681             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40682             return false;
40683         }
40684         if(this.vtype){
40685             var vt = Roo.form.VTypes;
40686             if(!vt[this.vtype](value, this)){
40687                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40688                 return false;
40689             }
40690         }
40691         if(typeof this.validator == "function"){
40692             var msg = this.validator(value);
40693             if(msg !== true){
40694                 this.markInvalid(msg);
40695                 return false;
40696             }
40697         }
40698         if(this.regex && !this.regex.test(value)){
40699             this.markInvalid(this.regexText);
40700             return false;
40701         }
40702         return true;
40703     },
40704
40705     /**
40706      * Selects text in this field
40707      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40708      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40709      */
40710     selectText : function(start, end){
40711         var v = this.getRawValue();
40712         if(v.length > 0){
40713             start = start === undefined ? 0 : start;
40714             end = end === undefined ? v.length : end;
40715             var d = this.el.dom;
40716             if(d.setSelectionRange){
40717                 d.setSelectionRange(start, end);
40718             }else if(d.createTextRange){
40719                 var range = d.createTextRange();
40720                 range.moveStart("character", start);
40721                 range.moveEnd("character", v.length-end);
40722                 range.select();
40723             }
40724         }
40725     },
40726
40727     /**
40728      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40729      * This only takes effect if grow = true, and fires the autosize event.
40730      */
40731     autoSize : function(){
40732         if(!this.grow || !this.rendered){
40733             return;
40734         }
40735         if(!this.metrics){
40736             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40737         }
40738         var el = this.el;
40739         var v = el.dom.value;
40740         var d = document.createElement('div');
40741         d.appendChild(document.createTextNode(v));
40742         v = d.innerHTML;
40743         d = null;
40744         v += "&#160;";
40745         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40746         this.el.setWidth(w);
40747         this.fireEvent("autosize", this, w);
40748     },
40749     
40750     // private
40751     SafariOnKeyDown : function(event)
40752     {
40753         // this is a workaround for a password hang bug on chrome/ webkit.
40754         
40755         var isSelectAll = false;
40756         
40757         if(this.el.dom.selectionEnd > 0){
40758             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40759         }
40760         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40761             event.preventDefault();
40762             this.setValue('');
40763             return;
40764         }
40765         
40766         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40767             
40768             event.preventDefault();
40769             // this is very hacky as keydown always get's upper case.
40770             
40771             var cc = String.fromCharCode(event.getCharCode());
40772             
40773             
40774             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40775             
40776         }
40777         
40778         
40779     }
40780 });/*
40781  * Based on:
40782  * Ext JS Library 1.1.1
40783  * Copyright(c) 2006-2007, Ext JS, LLC.
40784  *
40785  * Originally Released Under LGPL - original licence link has changed is not relivant.
40786  *
40787  * Fork - LGPL
40788  * <script type="text/javascript">
40789  */
40790  
40791 /**
40792  * @class Roo.form.Hidden
40793  * @extends Roo.form.TextField
40794  * Simple Hidden element used on forms 
40795  * 
40796  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40797  * 
40798  * @constructor
40799  * Creates a new Hidden form element.
40800  * @param {Object} config Configuration options
40801  */
40802
40803
40804
40805 // easy hidden field...
40806 Roo.form.Hidden = function(config){
40807     Roo.form.Hidden.superclass.constructor.call(this, config);
40808 };
40809   
40810 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40811     fieldLabel:      '',
40812     inputType:      'hidden',
40813     width:          50,
40814     allowBlank:     true,
40815     labelSeparator: '',
40816     hidden:         true,
40817     itemCls :       'x-form-item-display-none'
40818
40819
40820 });
40821
40822
40823 /*
40824  * Based on:
40825  * Ext JS Library 1.1.1
40826  * Copyright(c) 2006-2007, Ext JS, LLC.
40827  *
40828  * Originally Released Under LGPL - original licence link has changed is not relivant.
40829  *
40830  * Fork - LGPL
40831  * <script type="text/javascript">
40832  */
40833  
40834 /**
40835  * @class Roo.form.TriggerField
40836  * @extends Roo.form.TextField
40837  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40838  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40839  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40840  * for which you can provide a custom implementation.  For example:
40841  * <pre><code>
40842 var trigger = new Roo.form.TriggerField();
40843 trigger.onTriggerClick = myTriggerFn;
40844 trigger.applyTo('my-field');
40845 </code></pre>
40846  *
40847  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40848  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40849  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40850  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40851  * @constructor
40852  * Create a new TriggerField.
40853  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40854  * to the base TextField)
40855  */
40856 Roo.form.TriggerField = function(config){
40857     this.mimicing = false;
40858     Roo.form.TriggerField.superclass.constructor.call(this, config);
40859 };
40860
40861 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40862     /**
40863      * @cfg {String} triggerClass A CSS class to apply to the trigger
40864      */
40865     /**
40866      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40867      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40868      */
40869     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40870     /**
40871      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40872      */
40873     hideTrigger:false,
40874
40875     /** @cfg {Boolean} grow @hide */
40876     /** @cfg {Number} growMin @hide */
40877     /** @cfg {Number} growMax @hide */
40878
40879     /**
40880      * @hide 
40881      * @method
40882      */
40883     autoSize: Roo.emptyFn,
40884     // private
40885     monitorTab : true,
40886     // private
40887     deferHeight : true,
40888
40889     
40890     actionMode : 'wrap',
40891     // private
40892     onResize : function(w, h){
40893         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40894         if(typeof w == 'number'){
40895             var x = w - this.trigger.getWidth();
40896             this.el.setWidth(this.adjustWidth('input', x));
40897             this.trigger.setStyle('left', x+'px');
40898         }
40899     },
40900
40901     // private
40902     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40903
40904     // private
40905     getResizeEl : function(){
40906         return this.wrap;
40907     },
40908
40909     // private
40910     getPositionEl : function(){
40911         return this.wrap;
40912     },
40913
40914     // private
40915     alignErrorIcon : function(){
40916         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40917     },
40918
40919     // private
40920     onRender : function(ct, position){
40921         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40922         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40923         this.trigger = this.wrap.createChild(this.triggerConfig ||
40924                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40925         if(this.hideTrigger){
40926             this.trigger.setDisplayed(false);
40927         }
40928         this.initTrigger();
40929         if(!this.width){
40930             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40931         }
40932     },
40933
40934     // private
40935     initTrigger : function(){
40936         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40937         this.trigger.addClassOnOver('x-form-trigger-over');
40938         this.trigger.addClassOnClick('x-form-trigger-click');
40939     },
40940
40941     // private
40942     onDestroy : function(){
40943         if(this.trigger){
40944             this.trigger.removeAllListeners();
40945             this.trigger.remove();
40946         }
40947         if(this.wrap){
40948             this.wrap.remove();
40949         }
40950         Roo.form.TriggerField.superclass.onDestroy.call(this);
40951     },
40952
40953     // private
40954     onFocus : function(){
40955         Roo.form.TriggerField.superclass.onFocus.call(this);
40956         if(!this.mimicing){
40957             this.wrap.addClass('x-trigger-wrap-focus');
40958             this.mimicing = true;
40959             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40960             if(this.monitorTab){
40961                 this.el.on("keydown", this.checkTab, this);
40962             }
40963         }
40964     },
40965
40966     // private
40967     checkTab : function(e){
40968         if(e.getKey() == e.TAB){
40969             this.triggerBlur();
40970         }
40971     },
40972
40973     // private
40974     onBlur : function(){
40975         // do nothing
40976     },
40977
40978     // private
40979     mimicBlur : function(e, t){
40980         if(!this.wrap.contains(t) && this.validateBlur()){
40981             this.triggerBlur();
40982         }
40983     },
40984
40985     // private
40986     triggerBlur : function(){
40987         this.mimicing = false;
40988         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40989         if(this.monitorTab){
40990             this.el.un("keydown", this.checkTab, this);
40991         }
40992         this.wrap.removeClass('x-trigger-wrap-focus');
40993         Roo.form.TriggerField.superclass.onBlur.call(this);
40994     },
40995
40996     // private
40997     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40998     validateBlur : function(e, t){
40999         return true;
41000     },
41001
41002     // private
41003     onDisable : function(){
41004         Roo.form.TriggerField.superclass.onDisable.call(this);
41005         if(this.wrap){
41006             this.wrap.addClass('x-item-disabled');
41007         }
41008     },
41009
41010     // private
41011     onEnable : function(){
41012         Roo.form.TriggerField.superclass.onEnable.call(this);
41013         if(this.wrap){
41014             this.wrap.removeClass('x-item-disabled');
41015         }
41016     },
41017
41018     // private
41019     onShow : function(){
41020         var ae = this.getActionEl();
41021         
41022         if(ae){
41023             ae.dom.style.display = '';
41024             ae.dom.style.visibility = 'visible';
41025         }
41026     },
41027
41028     // private
41029     
41030     onHide : function(){
41031         var ae = this.getActionEl();
41032         ae.dom.style.display = 'none';
41033     },
41034
41035     /**
41036      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41037      * by an implementing function.
41038      * @method
41039      * @param {EventObject} e
41040      */
41041     onTriggerClick : Roo.emptyFn
41042 });
41043
41044 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41045 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41046 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41047 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41048     initComponent : function(){
41049         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41050
41051         this.triggerConfig = {
41052             tag:'span', cls:'x-form-twin-triggers', cn:[
41053             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41054             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41055         ]};
41056     },
41057
41058     getTrigger : function(index){
41059         return this.triggers[index];
41060     },
41061
41062     initTrigger : function(){
41063         var ts = this.trigger.select('.x-form-trigger', true);
41064         this.wrap.setStyle('overflow', 'hidden');
41065         var triggerField = this;
41066         ts.each(function(t, all, index){
41067             t.hide = function(){
41068                 var w = triggerField.wrap.getWidth();
41069                 this.dom.style.display = 'none';
41070                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41071             };
41072             t.show = function(){
41073                 var w = triggerField.wrap.getWidth();
41074                 this.dom.style.display = '';
41075                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41076             };
41077             var triggerIndex = 'Trigger'+(index+1);
41078
41079             if(this['hide'+triggerIndex]){
41080                 t.dom.style.display = 'none';
41081             }
41082             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41083             t.addClassOnOver('x-form-trigger-over');
41084             t.addClassOnClick('x-form-trigger-click');
41085         }, this);
41086         this.triggers = ts.elements;
41087     },
41088
41089     onTrigger1Click : Roo.emptyFn,
41090     onTrigger2Click : Roo.emptyFn
41091 });/*
41092  * Based on:
41093  * Ext JS Library 1.1.1
41094  * Copyright(c) 2006-2007, Ext JS, LLC.
41095  *
41096  * Originally Released Under LGPL - original licence link has changed is not relivant.
41097  *
41098  * Fork - LGPL
41099  * <script type="text/javascript">
41100  */
41101  
41102 /**
41103  * @class Roo.form.TextArea
41104  * @extends Roo.form.TextField
41105  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41106  * support for auto-sizing.
41107  * @constructor
41108  * Creates a new TextArea
41109  * @param {Object} config Configuration options
41110  */
41111 Roo.form.TextArea = function(config){
41112     Roo.form.TextArea.superclass.constructor.call(this, config);
41113     // these are provided exchanges for backwards compat
41114     // minHeight/maxHeight were replaced by growMin/growMax to be
41115     // compatible with TextField growing config values
41116     if(this.minHeight !== undefined){
41117         this.growMin = this.minHeight;
41118     }
41119     if(this.maxHeight !== undefined){
41120         this.growMax = this.maxHeight;
41121     }
41122 };
41123
41124 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41125     /**
41126      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41127      */
41128     growMin : 60,
41129     /**
41130      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41131      */
41132     growMax: 1000,
41133     /**
41134      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41135      * in the field (equivalent to setting overflow: hidden, defaults to false)
41136      */
41137     preventScrollbars: false,
41138     /**
41139      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41140      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41141      */
41142
41143     // private
41144     onRender : function(ct, position){
41145         if(!this.el){
41146             this.defaultAutoCreate = {
41147                 tag: "textarea",
41148                 style:"width:300px;height:60px;",
41149                 autocomplete: "new-password"
41150             };
41151         }
41152         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41153         if(this.grow){
41154             this.textSizeEl = Roo.DomHelper.append(document.body, {
41155                 tag: "pre", cls: "x-form-grow-sizer"
41156             });
41157             if(this.preventScrollbars){
41158                 this.el.setStyle("overflow", "hidden");
41159             }
41160             this.el.setHeight(this.growMin);
41161         }
41162     },
41163
41164     onDestroy : function(){
41165         if(this.textSizeEl){
41166             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41167         }
41168         Roo.form.TextArea.superclass.onDestroy.call(this);
41169     },
41170
41171     // private
41172     onKeyUp : function(e){
41173         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41174             this.autoSize();
41175         }
41176     },
41177
41178     /**
41179      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41180      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41181      */
41182     autoSize : function(){
41183         if(!this.grow || !this.textSizeEl){
41184             return;
41185         }
41186         var el = this.el;
41187         var v = el.dom.value;
41188         var ts = this.textSizeEl;
41189
41190         ts.innerHTML = '';
41191         ts.appendChild(document.createTextNode(v));
41192         v = ts.innerHTML;
41193
41194         Roo.fly(ts).setWidth(this.el.getWidth());
41195         if(v.length < 1){
41196             v = "&#160;&#160;";
41197         }else{
41198             if(Roo.isIE){
41199                 v = v.replace(/\n/g, '<p>&#160;</p>');
41200             }
41201             v += "&#160;\n&#160;";
41202         }
41203         ts.innerHTML = v;
41204         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41205         if(h != this.lastHeight){
41206             this.lastHeight = h;
41207             this.el.setHeight(h);
41208             this.fireEvent("autosize", this, h);
41209         }
41210     }
41211 });/*
41212  * Based on:
41213  * Ext JS Library 1.1.1
41214  * Copyright(c) 2006-2007, Ext JS, LLC.
41215  *
41216  * Originally Released Under LGPL - original licence link has changed is not relivant.
41217  *
41218  * Fork - LGPL
41219  * <script type="text/javascript">
41220  */
41221  
41222
41223 /**
41224  * @class Roo.form.NumberField
41225  * @extends Roo.form.TextField
41226  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41227  * @constructor
41228  * Creates a new NumberField
41229  * @param {Object} config Configuration options
41230  */
41231 Roo.form.NumberField = function(config){
41232     Roo.form.NumberField.superclass.constructor.call(this, config);
41233 };
41234
41235 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41236     /**
41237      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41238      */
41239     fieldClass: "x-form-field x-form-num-field",
41240     /**
41241      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41242      */
41243     allowDecimals : true,
41244     /**
41245      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41246      */
41247     decimalSeparator : ".",
41248     /**
41249      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41250      */
41251     decimalPrecision : 2,
41252     /**
41253      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41254      */
41255     allowNegative : true,
41256     /**
41257      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41258      */
41259     minValue : Number.NEGATIVE_INFINITY,
41260     /**
41261      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41262      */
41263     maxValue : Number.MAX_VALUE,
41264     /**
41265      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41266      */
41267     minText : "The minimum value for this field is {0}",
41268     /**
41269      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41270      */
41271     maxText : "The maximum value for this field is {0}",
41272     /**
41273      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41274      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41275      */
41276     nanText : "{0} is not a valid number",
41277
41278     // private
41279     initEvents : function(){
41280         Roo.form.NumberField.superclass.initEvents.call(this);
41281         var allowed = "0123456789";
41282         if(this.allowDecimals){
41283             allowed += this.decimalSeparator;
41284         }
41285         if(this.allowNegative){
41286             allowed += "-";
41287         }
41288         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41289         var keyPress = function(e){
41290             var k = e.getKey();
41291             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41292                 return;
41293             }
41294             var c = e.getCharCode();
41295             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41296                 e.stopEvent();
41297             }
41298         };
41299         this.el.on("keypress", keyPress, this);
41300     },
41301
41302     // private
41303     validateValue : function(value){
41304         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41305             return false;
41306         }
41307         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41308              return true;
41309         }
41310         var num = this.parseValue(value);
41311         if(isNaN(num)){
41312             this.markInvalid(String.format(this.nanText, value));
41313             return false;
41314         }
41315         if(num < this.minValue){
41316             this.markInvalid(String.format(this.minText, this.minValue));
41317             return false;
41318         }
41319         if(num > this.maxValue){
41320             this.markInvalid(String.format(this.maxText, this.maxValue));
41321             return false;
41322         }
41323         return true;
41324     },
41325
41326     getValue : function(){
41327         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41328     },
41329
41330     // private
41331     parseValue : function(value){
41332         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41333         return isNaN(value) ? '' : value;
41334     },
41335
41336     // private
41337     fixPrecision : function(value){
41338         var nan = isNaN(value);
41339         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41340             return nan ? '' : value;
41341         }
41342         return parseFloat(value).toFixed(this.decimalPrecision);
41343     },
41344
41345     setValue : function(v){
41346         v = this.fixPrecision(v);
41347         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41348     },
41349
41350     // private
41351     decimalPrecisionFcn : function(v){
41352         return Math.floor(v);
41353     },
41354
41355     beforeBlur : function(){
41356         var v = this.parseValue(this.getRawValue());
41357         if(v){
41358             this.setValue(v);
41359         }
41360     }
41361 });/*
41362  * Based on:
41363  * Ext JS Library 1.1.1
41364  * Copyright(c) 2006-2007, Ext JS, LLC.
41365  *
41366  * Originally Released Under LGPL - original licence link has changed is not relivant.
41367  *
41368  * Fork - LGPL
41369  * <script type="text/javascript">
41370  */
41371  
41372 /**
41373  * @class Roo.form.DateField
41374  * @extends Roo.form.TriggerField
41375  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41376 * @constructor
41377 * Create a new DateField
41378 * @param {Object} config
41379  */
41380 Roo.form.DateField = function(config)
41381 {
41382     Roo.form.DateField.superclass.constructor.call(this, config);
41383     
41384       this.addEvents({
41385          
41386         /**
41387          * @event select
41388          * Fires when a date is selected
41389              * @param {Roo.form.DateField} combo This combo box
41390              * @param {Date} date The date selected
41391              */
41392         'select' : true
41393          
41394     });
41395     
41396     
41397     if(typeof this.minValue == "string") {
41398         this.minValue = this.parseDate(this.minValue);
41399     }
41400     if(typeof this.maxValue == "string") {
41401         this.maxValue = this.parseDate(this.maxValue);
41402     }
41403     this.ddMatch = null;
41404     if(this.disabledDates){
41405         var dd = this.disabledDates;
41406         var re = "(?:";
41407         for(var i = 0; i < dd.length; i++){
41408             re += dd[i];
41409             if(i != dd.length-1) {
41410                 re += "|";
41411             }
41412         }
41413         this.ddMatch = new RegExp(re + ")");
41414     }
41415 };
41416
41417 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41418     /**
41419      * @cfg {String} format
41420      * The default date format string which can be overriden for localization support.  The format must be
41421      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41422      */
41423     format : "m/d/y",
41424     /**
41425      * @cfg {String} altFormats
41426      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41427      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41428      */
41429     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41430     /**
41431      * @cfg {Array} disabledDays
41432      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41433      */
41434     disabledDays : null,
41435     /**
41436      * @cfg {String} disabledDaysText
41437      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41438      */
41439     disabledDaysText : "Disabled",
41440     /**
41441      * @cfg {Array} disabledDates
41442      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41443      * expression so they are very powerful. Some examples:
41444      * <ul>
41445      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41446      * <li>["03/08", "09/16"] would disable those days for every year</li>
41447      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41448      * <li>["03/../2006"] would disable every day in March 2006</li>
41449      * <li>["^03"] would disable every day in every March</li>
41450      * </ul>
41451      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41452      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41453      */
41454     disabledDates : null,
41455     /**
41456      * @cfg {String} disabledDatesText
41457      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41458      */
41459     disabledDatesText : "Disabled",
41460     /**
41461      * @cfg {Date/String} minValue
41462      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41463      * valid format (defaults to null).
41464      */
41465     minValue : null,
41466     /**
41467      * @cfg {Date/String} maxValue
41468      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41469      * valid format (defaults to null).
41470      */
41471     maxValue : null,
41472     /**
41473      * @cfg {String} minText
41474      * The error text to display when the date in the cell is before minValue (defaults to
41475      * 'The date in this field must be after {minValue}').
41476      */
41477     minText : "The date in this field must be equal to or after {0}",
41478     /**
41479      * @cfg {String} maxText
41480      * The error text to display when the date in the cell is after maxValue (defaults to
41481      * 'The date in this field must be before {maxValue}').
41482      */
41483     maxText : "The date in this field must be equal to or before {0}",
41484     /**
41485      * @cfg {String} invalidText
41486      * The error text to display when the date in the field is invalid (defaults to
41487      * '{value} is not a valid date - it must be in the format {format}').
41488      */
41489     invalidText : "{0} is not a valid date - it must be in the format {1}",
41490     /**
41491      * @cfg {String} triggerClass
41492      * An additional CSS class used to style the trigger button.  The trigger will always get the
41493      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41494      * which displays a calendar icon).
41495      */
41496     triggerClass : 'x-form-date-trigger',
41497     
41498
41499     /**
41500      * @cfg {Boolean} useIso
41501      * if enabled, then the date field will use a hidden field to store the 
41502      * real value as iso formated date. default (false)
41503      */ 
41504     useIso : false,
41505     /**
41506      * @cfg {String/Object} autoCreate
41507      * A DomHelper element spec, or true for a default element spec (defaults to
41508      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41509      */ 
41510     // private
41511     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41512     
41513     // private
41514     hiddenField: false,
41515     
41516     onRender : function(ct, position)
41517     {
41518         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41519         if (this.useIso) {
41520             //this.el.dom.removeAttribute('name'); 
41521             Roo.log("Changing name?");
41522             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41523             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41524                     'before', true);
41525             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41526             // prevent input submission
41527             this.hiddenName = this.name;
41528         }
41529             
41530             
41531     },
41532     
41533     // private
41534     validateValue : function(value)
41535     {
41536         value = this.formatDate(value);
41537         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41538             Roo.log('super failed');
41539             return false;
41540         }
41541         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41542              return true;
41543         }
41544         var svalue = value;
41545         value = this.parseDate(value);
41546         if(!value){
41547             Roo.log('parse date failed' + svalue);
41548             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41549             return false;
41550         }
41551         var time = value.getTime();
41552         if(this.minValue && time < this.minValue.getTime()){
41553             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41554             return false;
41555         }
41556         if(this.maxValue && time > this.maxValue.getTime()){
41557             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41558             return false;
41559         }
41560         if(this.disabledDays){
41561             var day = value.getDay();
41562             for(var i = 0; i < this.disabledDays.length; i++) {
41563                 if(day === this.disabledDays[i]){
41564                     this.markInvalid(this.disabledDaysText);
41565                     return false;
41566                 }
41567             }
41568         }
41569         var fvalue = this.formatDate(value);
41570         if(this.ddMatch && this.ddMatch.test(fvalue)){
41571             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41572             return false;
41573         }
41574         return true;
41575     },
41576
41577     // private
41578     // Provides logic to override the default TriggerField.validateBlur which just returns true
41579     validateBlur : function(){
41580         return !this.menu || !this.menu.isVisible();
41581     },
41582     
41583     getName: function()
41584     {
41585         // returns hidden if it's set..
41586         if (!this.rendered) {return ''};
41587         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41588         
41589     },
41590
41591     /**
41592      * Returns the current date value of the date field.
41593      * @return {Date} The date value
41594      */
41595     getValue : function(){
41596         
41597         return  this.hiddenField ?
41598                 this.hiddenField.value :
41599                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41600     },
41601
41602     /**
41603      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41604      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41605      * (the default format used is "m/d/y").
41606      * <br />Usage:
41607      * <pre><code>
41608 //All of these calls set the same date value (May 4, 2006)
41609
41610 //Pass a date object:
41611 var dt = new Date('5/4/06');
41612 dateField.setValue(dt);
41613
41614 //Pass a date string (default format):
41615 dateField.setValue('5/4/06');
41616
41617 //Pass a date string (custom format):
41618 dateField.format = 'Y-m-d';
41619 dateField.setValue('2006-5-4');
41620 </code></pre>
41621      * @param {String/Date} date The date or valid date string
41622      */
41623     setValue : function(date){
41624         if (this.hiddenField) {
41625             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41626         }
41627         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41628         // make sure the value field is always stored as a date..
41629         this.value = this.parseDate(date);
41630         
41631         
41632     },
41633
41634     // private
41635     parseDate : function(value){
41636         if(!value || value instanceof Date){
41637             return value;
41638         }
41639         var v = Date.parseDate(value, this.format);
41640          if (!v && this.useIso) {
41641             v = Date.parseDate(value, 'Y-m-d');
41642         }
41643         if(!v && this.altFormats){
41644             if(!this.altFormatsArray){
41645                 this.altFormatsArray = this.altFormats.split("|");
41646             }
41647             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41648                 v = Date.parseDate(value, this.altFormatsArray[i]);
41649             }
41650         }
41651         return v;
41652     },
41653
41654     // private
41655     formatDate : function(date, fmt){
41656         return (!date || !(date instanceof Date)) ?
41657                date : date.dateFormat(fmt || this.format);
41658     },
41659
41660     // private
41661     menuListeners : {
41662         select: function(m, d){
41663             
41664             this.setValue(d);
41665             this.fireEvent('select', this, d);
41666         },
41667         show : function(){ // retain focus styling
41668             this.onFocus();
41669         },
41670         hide : function(){
41671             this.focus.defer(10, this);
41672             var ml = this.menuListeners;
41673             this.menu.un("select", ml.select,  this);
41674             this.menu.un("show", ml.show,  this);
41675             this.menu.un("hide", ml.hide,  this);
41676         }
41677     },
41678
41679     // private
41680     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41681     onTriggerClick : function(){
41682         if(this.disabled){
41683             return;
41684         }
41685         if(this.menu == null){
41686             this.menu = new Roo.menu.DateMenu();
41687         }
41688         Roo.apply(this.menu.picker,  {
41689             showClear: this.allowBlank,
41690             minDate : this.minValue,
41691             maxDate : this.maxValue,
41692             disabledDatesRE : this.ddMatch,
41693             disabledDatesText : this.disabledDatesText,
41694             disabledDays : this.disabledDays,
41695             disabledDaysText : this.disabledDaysText,
41696             format : this.useIso ? 'Y-m-d' : this.format,
41697             minText : String.format(this.minText, this.formatDate(this.minValue)),
41698             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41699         });
41700         this.menu.on(Roo.apply({}, this.menuListeners, {
41701             scope:this
41702         }));
41703         this.menu.picker.setValue(this.getValue() || new Date());
41704         this.menu.show(this.el, "tl-bl?");
41705     },
41706
41707     beforeBlur : function(){
41708         var v = this.parseDate(this.getRawValue());
41709         if(v){
41710             this.setValue(v);
41711         }
41712     },
41713
41714     /*@
41715      * overide
41716      * 
41717      */
41718     isDirty : function() {
41719         if(this.disabled) {
41720             return false;
41721         }
41722         
41723         if(typeof(this.startValue) === 'undefined'){
41724             return false;
41725         }
41726         
41727         return String(this.getValue()) !== String(this.startValue);
41728         
41729     },
41730     // @overide
41731     cleanLeadingSpace : function(e)
41732     {
41733        return;
41734     }
41735     
41736 });/*
41737  * Based on:
41738  * Ext JS Library 1.1.1
41739  * Copyright(c) 2006-2007, Ext JS, LLC.
41740  *
41741  * Originally Released Under LGPL - original licence link has changed is not relivant.
41742  *
41743  * Fork - LGPL
41744  * <script type="text/javascript">
41745  */
41746  
41747 /**
41748  * @class Roo.form.MonthField
41749  * @extends Roo.form.TriggerField
41750  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41751 * @constructor
41752 * Create a new MonthField
41753 * @param {Object} config
41754  */
41755 Roo.form.MonthField = function(config){
41756     
41757     Roo.form.MonthField.superclass.constructor.call(this, config);
41758     
41759       this.addEvents({
41760          
41761         /**
41762          * @event select
41763          * Fires when a date is selected
41764              * @param {Roo.form.MonthFieeld} combo This combo box
41765              * @param {Date} date The date selected
41766              */
41767         'select' : true
41768          
41769     });
41770     
41771     
41772     if(typeof this.minValue == "string") {
41773         this.minValue = this.parseDate(this.minValue);
41774     }
41775     if(typeof this.maxValue == "string") {
41776         this.maxValue = this.parseDate(this.maxValue);
41777     }
41778     this.ddMatch = null;
41779     if(this.disabledDates){
41780         var dd = this.disabledDates;
41781         var re = "(?:";
41782         for(var i = 0; i < dd.length; i++){
41783             re += dd[i];
41784             if(i != dd.length-1) {
41785                 re += "|";
41786             }
41787         }
41788         this.ddMatch = new RegExp(re + ")");
41789     }
41790 };
41791
41792 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41793     /**
41794      * @cfg {String} format
41795      * The default date format string which can be overriden for localization support.  The format must be
41796      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41797      */
41798     format : "M Y",
41799     /**
41800      * @cfg {String} altFormats
41801      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41802      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41803      */
41804     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41805     /**
41806      * @cfg {Array} disabledDays
41807      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41808      */
41809     disabledDays : [0,1,2,3,4,5,6],
41810     /**
41811      * @cfg {String} disabledDaysText
41812      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41813      */
41814     disabledDaysText : "Disabled",
41815     /**
41816      * @cfg {Array} disabledDates
41817      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41818      * expression so they are very powerful. Some examples:
41819      * <ul>
41820      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41821      * <li>["03/08", "09/16"] would disable those days for every year</li>
41822      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41823      * <li>["03/../2006"] would disable every day in March 2006</li>
41824      * <li>["^03"] would disable every day in every March</li>
41825      * </ul>
41826      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41827      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41828      */
41829     disabledDates : null,
41830     /**
41831      * @cfg {String} disabledDatesText
41832      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41833      */
41834     disabledDatesText : "Disabled",
41835     /**
41836      * @cfg {Date/String} minValue
41837      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41838      * valid format (defaults to null).
41839      */
41840     minValue : null,
41841     /**
41842      * @cfg {Date/String} maxValue
41843      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41844      * valid format (defaults to null).
41845      */
41846     maxValue : null,
41847     /**
41848      * @cfg {String} minText
41849      * The error text to display when the date in the cell is before minValue (defaults to
41850      * 'The date in this field must be after {minValue}').
41851      */
41852     minText : "The date in this field must be equal to or after {0}",
41853     /**
41854      * @cfg {String} maxTextf
41855      * The error text to display when the date in the cell is after maxValue (defaults to
41856      * 'The date in this field must be before {maxValue}').
41857      */
41858     maxText : "The date in this field must be equal to or before {0}",
41859     /**
41860      * @cfg {String} invalidText
41861      * The error text to display when the date in the field is invalid (defaults to
41862      * '{value} is not a valid date - it must be in the format {format}').
41863      */
41864     invalidText : "{0} is not a valid date - it must be in the format {1}",
41865     /**
41866      * @cfg {String} triggerClass
41867      * An additional CSS class used to style the trigger button.  The trigger will always get the
41868      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41869      * which displays a calendar icon).
41870      */
41871     triggerClass : 'x-form-date-trigger',
41872     
41873
41874     /**
41875      * @cfg {Boolean} useIso
41876      * if enabled, then the date field will use a hidden field to store the 
41877      * real value as iso formated date. default (true)
41878      */ 
41879     useIso : true,
41880     /**
41881      * @cfg {String/Object} autoCreate
41882      * A DomHelper element spec, or true for a default element spec (defaults to
41883      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41884      */ 
41885     // private
41886     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41887     
41888     // private
41889     hiddenField: false,
41890     
41891     hideMonthPicker : false,
41892     
41893     onRender : function(ct, position)
41894     {
41895         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41896         if (this.useIso) {
41897             this.el.dom.removeAttribute('name'); 
41898             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41899                     'before', true);
41900             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41901             // prevent input submission
41902             this.hiddenName = this.name;
41903         }
41904             
41905             
41906     },
41907     
41908     // private
41909     validateValue : function(value)
41910     {
41911         value = this.formatDate(value);
41912         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41913             return false;
41914         }
41915         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41916              return true;
41917         }
41918         var svalue = value;
41919         value = this.parseDate(value);
41920         if(!value){
41921             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41922             return false;
41923         }
41924         var time = value.getTime();
41925         if(this.minValue && time < this.minValue.getTime()){
41926             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41927             return false;
41928         }
41929         if(this.maxValue && time > this.maxValue.getTime()){
41930             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41931             return false;
41932         }
41933         /*if(this.disabledDays){
41934             var day = value.getDay();
41935             for(var i = 0; i < this.disabledDays.length; i++) {
41936                 if(day === this.disabledDays[i]){
41937                     this.markInvalid(this.disabledDaysText);
41938                     return false;
41939                 }
41940             }
41941         }
41942         */
41943         var fvalue = this.formatDate(value);
41944         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41945             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41946             return false;
41947         }
41948         */
41949         return true;
41950     },
41951
41952     // private
41953     // Provides logic to override the default TriggerField.validateBlur which just returns true
41954     validateBlur : function(){
41955         return !this.menu || !this.menu.isVisible();
41956     },
41957
41958     /**
41959      * Returns the current date value of the date field.
41960      * @return {Date} The date value
41961      */
41962     getValue : function(){
41963         
41964         
41965         
41966         return  this.hiddenField ?
41967                 this.hiddenField.value :
41968                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41969     },
41970
41971     /**
41972      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41973      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41974      * (the default format used is "m/d/y").
41975      * <br />Usage:
41976      * <pre><code>
41977 //All of these calls set the same date value (May 4, 2006)
41978
41979 //Pass a date object:
41980 var dt = new Date('5/4/06');
41981 monthField.setValue(dt);
41982
41983 //Pass a date string (default format):
41984 monthField.setValue('5/4/06');
41985
41986 //Pass a date string (custom format):
41987 monthField.format = 'Y-m-d';
41988 monthField.setValue('2006-5-4');
41989 </code></pre>
41990      * @param {String/Date} date The date or valid date string
41991      */
41992     setValue : function(date){
41993         Roo.log('month setValue' + date);
41994         // can only be first of month..
41995         
41996         var val = this.parseDate(date);
41997         
41998         if (this.hiddenField) {
41999             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42000         }
42001         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42002         this.value = this.parseDate(date);
42003     },
42004
42005     // private
42006     parseDate : function(value){
42007         if(!value || value instanceof Date){
42008             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42009             return value;
42010         }
42011         var v = Date.parseDate(value, this.format);
42012         if (!v && this.useIso) {
42013             v = Date.parseDate(value, 'Y-m-d');
42014         }
42015         if (v) {
42016             // 
42017             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42018         }
42019         
42020         
42021         if(!v && this.altFormats){
42022             if(!this.altFormatsArray){
42023                 this.altFormatsArray = this.altFormats.split("|");
42024             }
42025             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42026                 v = Date.parseDate(value, this.altFormatsArray[i]);
42027             }
42028         }
42029         return v;
42030     },
42031
42032     // private
42033     formatDate : function(date, fmt){
42034         return (!date || !(date instanceof Date)) ?
42035                date : date.dateFormat(fmt || this.format);
42036     },
42037
42038     // private
42039     menuListeners : {
42040         select: function(m, d){
42041             this.setValue(d);
42042             this.fireEvent('select', this, d);
42043         },
42044         show : function(){ // retain focus styling
42045             this.onFocus();
42046         },
42047         hide : function(){
42048             this.focus.defer(10, this);
42049             var ml = this.menuListeners;
42050             this.menu.un("select", ml.select,  this);
42051             this.menu.un("show", ml.show,  this);
42052             this.menu.un("hide", ml.hide,  this);
42053         }
42054     },
42055     // private
42056     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42057     onTriggerClick : function(){
42058         if(this.disabled){
42059             return;
42060         }
42061         if(this.menu == null){
42062             this.menu = new Roo.menu.DateMenu();
42063            
42064         }
42065         
42066         Roo.apply(this.menu.picker,  {
42067             
42068             showClear: this.allowBlank,
42069             minDate : this.minValue,
42070             maxDate : this.maxValue,
42071             disabledDatesRE : this.ddMatch,
42072             disabledDatesText : this.disabledDatesText,
42073             
42074             format : this.useIso ? 'Y-m-d' : this.format,
42075             minText : String.format(this.minText, this.formatDate(this.minValue)),
42076             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42077             
42078         });
42079          this.menu.on(Roo.apply({}, this.menuListeners, {
42080             scope:this
42081         }));
42082        
42083         
42084         var m = this.menu;
42085         var p = m.picker;
42086         
42087         // hide month picker get's called when we called by 'before hide';
42088         
42089         var ignorehide = true;
42090         p.hideMonthPicker  = function(disableAnim){
42091             if (ignorehide) {
42092                 return;
42093             }
42094              if(this.monthPicker){
42095                 Roo.log("hideMonthPicker called");
42096                 if(disableAnim === true){
42097                     this.monthPicker.hide();
42098                 }else{
42099                     this.monthPicker.slideOut('t', {duration:.2});
42100                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42101                     p.fireEvent("select", this, this.value);
42102                     m.hide();
42103                 }
42104             }
42105         }
42106         
42107         Roo.log('picker set value');
42108         Roo.log(this.getValue());
42109         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42110         m.show(this.el, 'tl-bl?');
42111         ignorehide  = false;
42112         // this will trigger hideMonthPicker..
42113         
42114         
42115         // hidden the day picker
42116         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42117         
42118         
42119         
42120       
42121         
42122         p.showMonthPicker.defer(100, p);
42123     
42124         
42125        
42126     },
42127
42128     beforeBlur : function(){
42129         var v = this.parseDate(this.getRawValue());
42130         if(v){
42131             this.setValue(v);
42132         }
42133     }
42134
42135     /** @cfg {Boolean} grow @hide */
42136     /** @cfg {Number} growMin @hide */
42137     /** @cfg {Number} growMax @hide */
42138     /**
42139      * @hide
42140      * @method autoSize
42141      */
42142 });/*
42143  * Based on:
42144  * Ext JS Library 1.1.1
42145  * Copyright(c) 2006-2007, Ext JS, LLC.
42146  *
42147  * Originally Released Under LGPL - original licence link has changed is not relivant.
42148  *
42149  * Fork - LGPL
42150  * <script type="text/javascript">
42151  */
42152  
42153
42154 /**
42155  * @class Roo.form.ComboBox
42156  * @extends Roo.form.TriggerField
42157  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42158  * @constructor
42159  * Create a new ComboBox.
42160  * @param {Object} config Configuration options
42161  */
42162 Roo.form.ComboBox = function(config){
42163     Roo.form.ComboBox.superclass.constructor.call(this, config);
42164     this.addEvents({
42165         /**
42166          * @event expand
42167          * Fires when the dropdown list is expanded
42168              * @param {Roo.form.ComboBox} combo This combo box
42169              */
42170         'expand' : true,
42171         /**
42172          * @event collapse
42173          * Fires when the dropdown list is collapsed
42174              * @param {Roo.form.ComboBox} combo This combo box
42175              */
42176         'collapse' : true,
42177         /**
42178          * @event beforeselect
42179          * Fires before a list item is selected. Return false to cancel the selection.
42180              * @param {Roo.form.ComboBox} combo This combo box
42181              * @param {Roo.data.Record} record The data record returned from the underlying store
42182              * @param {Number} index The index of the selected item in the dropdown list
42183              */
42184         'beforeselect' : true,
42185         /**
42186          * @event select
42187          * Fires when a list item is selected
42188              * @param {Roo.form.ComboBox} combo This combo box
42189              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42190              * @param {Number} index The index of the selected item in the dropdown list
42191              */
42192         'select' : true,
42193         /**
42194          * @event beforequery
42195          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42196          * The event object passed has these properties:
42197              * @param {Roo.form.ComboBox} combo This combo box
42198              * @param {String} query The query
42199              * @param {Boolean} forceAll true to force "all" query
42200              * @param {Boolean} cancel true to cancel the query
42201              * @param {Object} e The query event object
42202              */
42203         'beforequery': true,
42204          /**
42205          * @event add
42206          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42207              * @param {Roo.form.ComboBox} combo This combo box
42208              */
42209         'add' : true,
42210         /**
42211          * @event edit
42212          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42213              * @param {Roo.form.ComboBox} combo This combo box
42214              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42215              */
42216         'edit' : true
42217         
42218         
42219     });
42220     if(this.transform){
42221         this.allowDomMove = false;
42222         var s = Roo.getDom(this.transform);
42223         if(!this.hiddenName){
42224             this.hiddenName = s.name;
42225         }
42226         if(!this.store){
42227             this.mode = 'local';
42228             var d = [], opts = s.options;
42229             for(var i = 0, len = opts.length;i < len; i++){
42230                 var o = opts[i];
42231                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42232                 if(o.selected) {
42233                     this.value = value;
42234                 }
42235                 d.push([value, o.text]);
42236             }
42237             this.store = new Roo.data.SimpleStore({
42238                 'id': 0,
42239                 fields: ['value', 'text'],
42240                 data : d
42241             });
42242             this.valueField = 'value';
42243             this.displayField = 'text';
42244         }
42245         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42246         if(!this.lazyRender){
42247             this.target = true;
42248             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42249             s.parentNode.removeChild(s); // remove it
42250             this.render(this.el.parentNode);
42251         }else{
42252             s.parentNode.removeChild(s); // remove it
42253         }
42254
42255     }
42256     if (this.store) {
42257         this.store = Roo.factory(this.store, Roo.data);
42258     }
42259     
42260     this.selectedIndex = -1;
42261     if(this.mode == 'local'){
42262         if(config.queryDelay === undefined){
42263             this.queryDelay = 10;
42264         }
42265         if(config.minChars === undefined){
42266             this.minChars = 0;
42267         }
42268     }
42269 };
42270
42271 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42272     /**
42273      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42274      */
42275     /**
42276      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42277      * rendering into an Roo.Editor, defaults to false)
42278      */
42279     /**
42280      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42281      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42282      */
42283     /**
42284      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42285      */
42286     /**
42287      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42288      * the dropdown list (defaults to undefined, with no header element)
42289      */
42290
42291      /**
42292      * @cfg {String/Roo.Template} tpl The template to use to render the output
42293      */
42294      
42295     // private
42296     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42297     /**
42298      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42299      */
42300     listWidth: undefined,
42301     /**
42302      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42303      * mode = 'remote' or 'text' if mode = 'local')
42304      */
42305     displayField: undefined,
42306     /**
42307      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42308      * mode = 'remote' or 'value' if mode = 'local'). 
42309      * Note: use of a valueField requires the user make a selection
42310      * in order for a value to be mapped.
42311      */
42312     valueField: undefined,
42313     
42314     
42315     /**
42316      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42317      * field's data value (defaults to the underlying DOM element's name)
42318      */
42319     hiddenName: undefined,
42320     /**
42321      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42322      */
42323     listClass: '',
42324     /**
42325      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42326      */
42327     selectedClass: 'x-combo-selected',
42328     /**
42329      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42330      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42331      * which displays a downward arrow icon).
42332      */
42333     triggerClass : 'x-form-arrow-trigger',
42334     /**
42335      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42336      */
42337     shadow:'sides',
42338     /**
42339      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42340      * anchor positions (defaults to 'tl-bl')
42341      */
42342     listAlign: 'tl-bl?',
42343     /**
42344      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42345      */
42346     maxHeight: 300,
42347     /**
42348      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42349      * query specified by the allQuery config option (defaults to 'query')
42350      */
42351     triggerAction: 'query',
42352     /**
42353      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42354      * (defaults to 4, does not apply if editable = false)
42355      */
42356     minChars : 4,
42357     /**
42358      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42359      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42360      */
42361     typeAhead: false,
42362     /**
42363      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42364      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42365      */
42366     queryDelay: 500,
42367     /**
42368      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42369      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42370      */
42371     pageSize: 0,
42372     /**
42373      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42374      * when editable = true (defaults to false)
42375      */
42376     selectOnFocus:false,
42377     /**
42378      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42379      */
42380     queryParam: 'query',
42381     /**
42382      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42383      * when mode = 'remote' (defaults to 'Loading...')
42384      */
42385     loadingText: 'Loading...',
42386     /**
42387      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42388      */
42389     resizable: false,
42390     /**
42391      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42392      */
42393     handleHeight : 8,
42394     /**
42395      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42396      * traditional select (defaults to true)
42397      */
42398     editable: true,
42399     /**
42400      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42401      */
42402     allQuery: '',
42403     /**
42404      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42405      */
42406     mode: 'remote',
42407     /**
42408      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42409      * listWidth has a higher value)
42410      */
42411     minListWidth : 70,
42412     /**
42413      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42414      * allow the user to set arbitrary text into the field (defaults to false)
42415      */
42416     forceSelection:false,
42417     /**
42418      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42419      * if typeAhead = true (defaults to 250)
42420      */
42421     typeAheadDelay : 250,
42422     /**
42423      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42424      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42425      */
42426     valueNotFoundText : undefined,
42427     /**
42428      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42429      */
42430     blockFocus : false,
42431     
42432     /**
42433      * @cfg {Boolean} disableClear Disable showing of clear button.
42434      */
42435     disableClear : false,
42436     /**
42437      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42438      */
42439     alwaysQuery : false,
42440     
42441     //private
42442     addicon : false,
42443     editicon: false,
42444     
42445     // element that contains real text value.. (when hidden is used..)
42446      
42447     // private
42448     onRender : function(ct, position)
42449     {
42450         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42451         
42452         if(this.hiddenName){
42453             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42454                     'before', true);
42455             this.hiddenField.value =
42456                 this.hiddenValue !== undefined ? this.hiddenValue :
42457                 this.value !== undefined ? this.value : '';
42458
42459             // prevent input submission
42460             this.el.dom.removeAttribute('name');
42461              
42462              
42463         }
42464         
42465         if(Roo.isGecko){
42466             this.el.dom.setAttribute('autocomplete', 'off');
42467         }
42468
42469         var cls = 'x-combo-list';
42470
42471         this.list = new Roo.Layer({
42472             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42473         });
42474
42475         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42476         this.list.setWidth(lw);
42477         this.list.swallowEvent('mousewheel');
42478         this.assetHeight = 0;
42479
42480         if(this.title){
42481             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42482             this.assetHeight += this.header.getHeight();
42483         }
42484
42485         this.innerList = this.list.createChild({cls:cls+'-inner'});
42486         this.innerList.on('mouseover', this.onViewOver, this);
42487         this.innerList.on('mousemove', this.onViewMove, this);
42488         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42489         
42490         if(this.allowBlank && !this.pageSize && !this.disableClear){
42491             this.footer = this.list.createChild({cls:cls+'-ft'});
42492             this.pageTb = new Roo.Toolbar(this.footer);
42493            
42494         }
42495         if(this.pageSize){
42496             this.footer = this.list.createChild({cls:cls+'-ft'});
42497             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42498                     {pageSize: this.pageSize});
42499             
42500         }
42501         
42502         if (this.pageTb && this.allowBlank && !this.disableClear) {
42503             var _this = this;
42504             this.pageTb.add(new Roo.Toolbar.Fill(), {
42505                 cls: 'x-btn-icon x-btn-clear',
42506                 text: '&#160;',
42507                 handler: function()
42508                 {
42509                     _this.collapse();
42510                     _this.clearValue();
42511                     _this.onSelect(false, -1);
42512                 }
42513             });
42514         }
42515         if (this.footer) {
42516             this.assetHeight += this.footer.getHeight();
42517         }
42518         
42519
42520         if(!this.tpl){
42521             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42522         }
42523
42524         this.view = new Roo.View(this.innerList, this.tpl, {
42525             singleSelect:true,
42526             store: this.store,
42527             selectedClass: this.selectedClass
42528         });
42529
42530         this.view.on('click', this.onViewClick, this);
42531
42532         this.store.on('beforeload', this.onBeforeLoad, this);
42533         this.store.on('load', this.onLoad, this);
42534         this.store.on('loadexception', this.onLoadException, this);
42535
42536         if(this.resizable){
42537             this.resizer = new Roo.Resizable(this.list,  {
42538                pinned:true, handles:'se'
42539             });
42540             this.resizer.on('resize', function(r, w, h){
42541                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42542                 this.listWidth = w;
42543                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42544                 this.restrictHeight();
42545             }, this);
42546             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42547         }
42548         if(!this.editable){
42549             this.editable = true;
42550             this.setEditable(false);
42551         }  
42552         
42553         
42554         if (typeof(this.events.add.listeners) != 'undefined') {
42555             
42556             this.addicon = this.wrap.createChild(
42557                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42558        
42559             this.addicon.on('click', function(e) {
42560                 this.fireEvent('add', this);
42561             }, this);
42562         }
42563         if (typeof(this.events.edit.listeners) != 'undefined') {
42564             
42565             this.editicon = this.wrap.createChild(
42566                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42567             if (this.addicon) {
42568                 this.editicon.setStyle('margin-left', '40px');
42569             }
42570             this.editicon.on('click', function(e) {
42571                 
42572                 // we fire even  if inothing is selected..
42573                 this.fireEvent('edit', this, this.lastData );
42574                 
42575             }, this);
42576         }
42577         
42578         
42579         
42580     },
42581
42582     // private
42583     initEvents : function(){
42584         Roo.form.ComboBox.superclass.initEvents.call(this);
42585
42586         this.keyNav = new Roo.KeyNav(this.el, {
42587             "up" : function(e){
42588                 this.inKeyMode = true;
42589                 this.selectPrev();
42590             },
42591
42592             "down" : function(e){
42593                 if(!this.isExpanded()){
42594                     this.onTriggerClick();
42595                 }else{
42596                     this.inKeyMode = true;
42597                     this.selectNext();
42598                 }
42599             },
42600
42601             "enter" : function(e){
42602                 this.onViewClick();
42603                 //return true;
42604             },
42605
42606             "esc" : function(e){
42607                 this.collapse();
42608             },
42609
42610             "tab" : function(e){
42611                 this.onViewClick(false);
42612                 this.fireEvent("specialkey", this, e);
42613                 return true;
42614             },
42615
42616             scope : this,
42617
42618             doRelay : function(foo, bar, hname){
42619                 if(hname == 'down' || this.scope.isExpanded()){
42620                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42621                 }
42622                 return true;
42623             },
42624
42625             forceKeyDown: true
42626         });
42627         this.queryDelay = Math.max(this.queryDelay || 10,
42628                 this.mode == 'local' ? 10 : 250);
42629         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42630         if(this.typeAhead){
42631             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42632         }
42633         if(this.editable !== false){
42634             this.el.on("keyup", this.onKeyUp, this);
42635         }
42636         if(this.forceSelection){
42637             this.on('blur', this.doForce, this);
42638         }
42639     },
42640
42641     onDestroy : function(){
42642         if(this.view){
42643             this.view.setStore(null);
42644             this.view.el.removeAllListeners();
42645             this.view.el.remove();
42646             this.view.purgeListeners();
42647         }
42648         if(this.list){
42649             this.list.destroy();
42650         }
42651         if(this.store){
42652             this.store.un('beforeload', this.onBeforeLoad, this);
42653             this.store.un('load', this.onLoad, this);
42654             this.store.un('loadexception', this.onLoadException, this);
42655         }
42656         Roo.form.ComboBox.superclass.onDestroy.call(this);
42657     },
42658
42659     // private
42660     fireKey : function(e){
42661         if(e.isNavKeyPress() && !this.list.isVisible()){
42662             this.fireEvent("specialkey", this, e);
42663         }
42664     },
42665
42666     // private
42667     onResize: function(w, h){
42668         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42669         
42670         if(typeof w != 'number'){
42671             // we do not handle it!?!?
42672             return;
42673         }
42674         var tw = this.trigger.getWidth();
42675         tw += this.addicon ? this.addicon.getWidth() : 0;
42676         tw += this.editicon ? this.editicon.getWidth() : 0;
42677         var x = w - tw;
42678         this.el.setWidth( this.adjustWidth('input', x));
42679             
42680         this.trigger.setStyle('left', x+'px');
42681         
42682         if(this.list && this.listWidth === undefined){
42683             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42684             this.list.setWidth(lw);
42685             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42686         }
42687         
42688     
42689         
42690     },
42691
42692     /**
42693      * Allow or prevent the user from directly editing the field text.  If false is passed,
42694      * the user will only be able to select from the items defined in the dropdown list.  This method
42695      * is the runtime equivalent of setting the 'editable' config option at config time.
42696      * @param {Boolean} value True to allow the user to directly edit the field text
42697      */
42698     setEditable : function(value){
42699         if(value == this.editable){
42700             return;
42701         }
42702         this.editable = value;
42703         if(!value){
42704             this.el.dom.setAttribute('readOnly', true);
42705             this.el.on('mousedown', this.onTriggerClick,  this);
42706             this.el.addClass('x-combo-noedit');
42707         }else{
42708             this.el.dom.setAttribute('readOnly', false);
42709             this.el.un('mousedown', this.onTriggerClick,  this);
42710             this.el.removeClass('x-combo-noedit');
42711         }
42712     },
42713
42714     // private
42715     onBeforeLoad : function(){
42716         if(!this.hasFocus){
42717             return;
42718         }
42719         this.innerList.update(this.loadingText ?
42720                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42721         this.restrictHeight();
42722         this.selectedIndex = -1;
42723     },
42724
42725     // private
42726     onLoad : function(){
42727         if(!this.hasFocus){
42728             return;
42729         }
42730         if(this.store.getCount() > 0){
42731             this.expand();
42732             this.restrictHeight();
42733             if(this.lastQuery == this.allQuery){
42734                 if(this.editable){
42735                     this.el.dom.select();
42736                 }
42737                 if(!this.selectByValue(this.value, true)){
42738                     this.select(0, true);
42739                 }
42740             }else{
42741                 this.selectNext();
42742                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42743                     this.taTask.delay(this.typeAheadDelay);
42744                 }
42745             }
42746         }else{
42747             this.onEmptyResults();
42748         }
42749         //this.el.focus();
42750     },
42751     // private
42752     onLoadException : function()
42753     {
42754         this.collapse();
42755         Roo.log(this.store.reader.jsonData);
42756         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42757             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42758         }
42759         
42760         
42761     },
42762     // private
42763     onTypeAhead : function(){
42764         if(this.store.getCount() > 0){
42765             var r = this.store.getAt(0);
42766             var newValue = r.data[this.displayField];
42767             var len = newValue.length;
42768             var selStart = this.getRawValue().length;
42769             if(selStart != len){
42770                 this.setRawValue(newValue);
42771                 this.selectText(selStart, newValue.length);
42772             }
42773         }
42774     },
42775
42776     // private
42777     onSelect : function(record, index){
42778         if(this.fireEvent('beforeselect', this, record, index) !== false){
42779             this.setFromData(index > -1 ? record.data : false);
42780             this.collapse();
42781             this.fireEvent('select', this, record, index);
42782         }
42783     },
42784
42785     /**
42786      * Returns the currently selected field value or empty string if no value is set.
42787      * @return {String} value The selected value
42788      */
42789     getValue : function(){
42790         if(this.valueField){
42791             return typeof this.value != 'undefined' ? this.value : '';
42792         }
42793         return Roo.form.ComboBox.superclass.getValue.call(this);
42794     },
42795
42796     /**
42797      * Clears any text/value currently set in the field
42798      */
42799     clearValue : function(){
42800         if(this.hiddenField){
42801             this.hiddenField.value = '';
42802         }
42803         this.value = '';
42804         this.setRawValue('');
42805         this.lastSelectionText = '';
42806         
42807     },
42808
42809     /**
42810      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42811      * will be displayed in the field.  If the value does not match the data value of an existing item,
42812      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42813      * Otherwise the field will be blank (although the value will still be set).
42814      * @param {String} value The value to match
42815      */
42816     setValue : function(v){
42817         var text = v;
42818         if(this.valueField){
42819             var r = this.findRecord(this.valueField, v);
42820             if(r){
42821                 text = r.data[this.displayField];
42822             }else if(this.valueNotFoundText !== undefined){
42823                 text = this.valueNotFoundText;
42824             }
42825         }
42826         this.lastSelectionText = text;
42827         if(this.hiddenField){
42828             this.hiddenField.value = v;
42829         }
42830         Roo.form.ComboBox.superclass.setValue.call(this, text);
42831         this.value = v;
42832     },
42833     /**
42834      * @property {Object} the last set data for the element
42835      */
42836     
42837     lastData : false,
42838     /**
42839      * Sets the value of the field based on a object which is related to the record format for the store.
42840      * @param {Object} value the value to set as. or false on reset?
42841      */
42842     setFromData : function(o){
42843         var dv = ''; // display value
42844         var vv = ''; // value value..
42845         this.lastData = o;
42846         if (this.displayField) {
42847             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42848         } else {
42849             // this is an error condition!!!
42850             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42851         }
42852         
42853         if(this.valueField){
42854             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42855         }
42856         if(this.hiddenField){
42857             this.hiddenField.value = vv;
42858             
42859             this.lastSelectionText = dv;
42860             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42861             this.value = vv;
42862             return;
42863         }
42864         // no hidden field.. - we store the value in 'value', but still display
42865         // display field!!!!
42866         this.lastSelectionText = dv;
42867         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42868         this.value = vv;
42869         
42870         
42871     },
42872     // private
42873     reset : function(){
42874         // overridden so that last data is reset..
42875         this.setValue(this.resetValue);
42876         this.originalValue = this.getValue();
42877         this.clearInvalid();
42878         this.lastData = false;
42879         if (this.view) {
42880             this.view.clearSelections();
42881         }
42882     },
42883     // private
42884     findRecord : function(prop, value){
42885         var record;
42886         if(this.store.getCount() > 0){
42887             this.store.each(function(r){
42888                 if(r.data[prop] == value){
42889                     record = r;
42890                     return false;
42891                 }
42892                 return true;
42893             });
42894         }
42895         return record;
42896     },
42897     
42898     getName: function()
42899     {
42900         // returns hidden if it's set..
42901         if (!this.rendered) {return ''};
42902         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42903         
42904     },
42905     // private
42906     onViewMove : function(e, t){
42907         this.inKeyMode = false;
42908     },
42909
42910     // private
42911     onViewOver : function(e, t){
42912         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42913             return;
42914         }
42915         var item = this.view.findItemFromChild(t);
42916         if(item){
42917             var index = this.view.indexOf(item);
42918             this.select(index, false);
42919         }
42920     },
42921
42922     // private
42923     onViewClick : function(doFocus)
42924     {
42925         var index = this.view.getSelectedIndexes()[0];
42926         var r = this.store.getAt(index);
42927         if(r){
42928             this.onSelect(r, index);
42929         }
42930         if(doFocus !== false && !this.blockFocus){
42931             this.el.focus();
42932         }
42933     },
42934
42935     // private
42936     restrictHeight : function(){
42937         this.innerList.dom.style.height = '';
42938         var inner = this.innerList.dom;
42939         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42940         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42941         this.list.beginUpdate();
42942         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42943         this.list.alignTo(this.el, this.listAlign);
42944         this.list.endUpdate();
42945     },
42946
42947     // private
42948     onEmptyResults : function(){
42949         this.collapse();
42950     },
42951
42952     /**
42953      * Returns true if the dropdown list is expanded, else false.
42954      */
42955     isExpanded : function(){
42956         return this.list.isVisible();
42957     },
42958
42959     /**
42960      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42961      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42962      * @param {String} value The data value of the item to select
42963      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42964      * selected item if it is not currently in view (defaults to true)
42965      * @return {Boolean} True if the value matched an item in the list, else false
42966      */
42967     selectByValue : function(v, scrollIntoView){
42968         if(v !== undefined && v !== null){
42969             var r = this.findRecord(this.valueField || this.displayField, v);
42970             if(r){
42971                 this.select(this.store.indexOf(r), scrollIntoView);
42972                 return true;
42973             }
42974         }
42975         return false;
42976     },
42977
42978     /**
42979      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42980      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42981      * @param {Number} index The zero-based index of the list item to select
42982      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42983      * selected item if it is not currently in view (defaults to true)
42984      */
42985     select : function(index, scrollIntoView){
42986         this.selectedIndex = index;
42987         this.view.select(index);
42988         if(scrollIntoView !== false){
42989             var el = this.view.getNode(index);
42990             if(el){
42991                 this.innerList.scrollChildIntoView(el, false);
42992             }
42993         }
42994     },
42995
42996     // private
42997     selectNext : function(){
42998         var ct = this.store.getCount();
42999         if(ct > 0){
43000             if(this.selectedIndex == -1){
43001                 this.select(0);
43002             }else if(this.selectedIndex < ct-1){
43003                 this.select(this.selectedIndex+1);
43004             }
43005         }
43006     },
43007
43008     // private
43009     selectPrev : function(){
43010         var ct = this.store.getCount();
43011         if(ct > 0){
43012             if(this.selectedIndex == -1){
43013                 this.select(0);
43014             }else if(this.selectedIndex != 0){
43015                 this.select(this.selectedIndex-1);
43016             }
43017         }
43018     },
43019
43020     // private
43021     onKeyUp : function(e){
43022         if(this.editable !== false && !e.isSpecialKey()){
43023             this.lastKey = e.getKey();
43024             this.dqTask.delay(this.queryDelay);
43025         }
43026     },
43027
43028     // private
43029     validateBlur : function(){
43030         return !this.list || !this.list.isVisible();   
43031     },
43032
43033     // private
43034     initQuery : function(){
43035         this.doQuery(this.getRawValue());
43036     },
43037
43038     // private
43039     doForce : function(){
43040         if(this.el.dom.value.length > 0){
43041             this.el.dom.value =
43042                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43043              
43044         }
43045     },
43046
43047     /**
43048      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43049      * query allowing the query action to be canceled if needed.
43050      * @param {String} query The SQL query to execute
43051      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43052      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43053      * saved in the current store (defaults to false)
43054      */
43055     doQuery : function(q, forceAll){
43056         if(q === undefined || q === null){
43057             q = '';
43058         }
43059         var qe = {
43060             query: q,
43061             forceAll: forceAll,
43062             combo: this,
43063             cancel:false
43064         };
43065         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43066             return false;
43067         }
43068         q = qe.query;
43069         forceAll = qe.forceAll;
43070         if(forceAll === true || (q.length >= this.minChars)){
43071             if(this.lastQuery != q || this.alwaysQuery){
43072                 this.lastQuery = q;
43073                 if(this.mode == 'local'){
43074                     this.selectedIndex = -1;
43075                     if(forceAll){
43076                         this.store.clearFilter();
43077                     }else{
43078                         this.store.filter(this.displayField, q);
43079                     }
43080                     this.onLoad();
43081                 }else{
43082                     this.store.baseParams[this.queryParam] = q;
43083                     this.store.load({
43084                         params: this.getParams(q)
43085                     });
43086                     this.expand();
43087                 }
43088             }else{
43089                 this.selectedIndex = -1;
43090                 this.onLoad();   
43091             }
43092         }
43093     },
43094
43095     // private
43096     getParams : function(q){
43097         var p = {};
43098         //p[this.queryParam] = q;
43099         if(this.pageSize){
43100             p.start = 0;
43101             p.limit = this.pageSize;
43102         }
43103         return p;
43104     },
43105
43106     /**
43107      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43108      */
43109     collapse : function(){
43110         if(!this.isExpanded()){
43111             return;
43112         }
43113         this.list.hide();
43114         Roo.get(document).un('mousedown', this.collapseIf, this);
43115         Roo.get(document).un('mousewheel', this.collapseIf, this);
43116         if (!this.editable) {
43117             Roo.get(document).un('keydown', this.listKeyPress, this);
43118         }
43119         this.fireEvent('collapse', this);
43120     },
43121
43122     // private
43123     collapseIf : function(e){
43124         if(!e.within(this.wrap) && !e.within(this.list)){
43125             this.collapse();
43126         }
43127     },
43128
43129     /**
43130      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43131      */
43132     expand : function(){
43133         if(this.isExpanded() || !this.hasFocus){
43134             return;
43135         }
43136         this.list.alignTo(this.el, this.listAlign);
43137         this.list.show();
43138         Roo.get(document).on('mousedown', this.collapseIf, this);
43139         Roo.get(document).on('mousewheel', this.collapseIf, this);
43140         if (!this.editable) {
43141             Roo.get(document).on('keydown', this.listKeyPress, this);
43142         }
43143         
43144         this.fireEvent('expand', this);
43145     },
43146
43147     // private
43148     // Implements the default empty TriggerField.onTriggerClick function
43149     onTriggerClick : function(){
43150         if(this.disabled){
43151             return;
43152         }
43153         if(this.isExpanded()){
43154             this.collapse();
43155             if (!this.blockFocus) {
43156                 this.el.focus();
43157             }
43158             
43159         }else {
43160             this.hasFocus = true;
43161             if(this.triggerAction == 'all') {
43162                 this.doQuery(this.allQuery, true);
43163             } else {
43164                 this.doQuery(this.getRawValue());
43165             }
43166             if (!this.blockFocus) {
43167                 this.el.focus();
43168             }
43169         }
43170     },
43171     listKeyPress : function(e)
43172     {
43173         //Roo.log('listkeypress');
43174         // scroll to first matching element based on key pres..
43175         if (e.isSpecialKey()) {
43176             return false;
43177         }
43178         var k = String.fromCharCode(e.getKey()).toUpperCase();
43179         //Roo.log(k);
43180         var match  = false;
43181         var csel = this.view.getSelectedNodes();
43182         var cselitem = false;
43183         if (csel.length) {
43184             var ix = this.view.indexOf(csel[0]);
43185             cselitem  = this.store.getAt(ix);
43186             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43187                 cselitem = false;
43188             }
43189             
43190         }
43191         
43192         this.store.each(function(v) { 
43193             if (cselitem) {
43194                 // start at existing selection.
43195                 if (cselitem.id == v.id) {
43196                     cselitem = false;
43197                 }
43198                 return;
43199             }
43200                 
43201             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43202                 match = this.store.indexOf(v);
43203                 return false;
43204             }
43205         }, this);
43206         
43207         if (match === false) {
43208             return true; // no more action?
43209         }
43210         // scroll to?
43211         this.view.select(match);
43212         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43213         sn.scrollIntoView(sn.dom.parentNode, false);
43214     } 
43215
43216     /** 
43217     * @cfg {Boolean} grow 
43218     * @hide 
43219     */
43220     /** 
43221     * @cfg {Number} growMin 
43222     * @hide 
43223     */
43224     /** 
43225     * @cfg {Number} growMax 
43226     * @hide 
43227     */
43228     /**
43229      * @hide
43230      * @method autoSize
43231      */
43232 });/*
43233  * Copyright(c) 2010-2012, Roo J Solutions Limited
43234  *
43235  * Licence LGPL
43236  *
43237  */
43238
43239 /**
43240  * @class Roo.form.ComboBoxArray
43241  * @extends Roo.form.TextField
43242  * A facebook style adder... for lists of email / people / countries  etc...
43243  * pick multiple items from a combo box, and shows each one.
43244  *
43245  *  Fred [x]  Brian [x]  [Pick another |v]
43246  *
43247  *
43248  *  For this to work: it needs various extra information
43249  *    - normal combo problay has
43250  *      name, hiddenName
43251  *    + displayField, valueField
43252  *
43253  *    For our purpose...
43254  *
43255  *
43256  *   If we change from 'extends' to wrapping...
43257  *   
43258  *  
43259  *
43260  
43261  
43262  * @constructor
43263  * Create a new ComboBoxArray.
43264  * @param {Object} config Configuration options
43265  */
43266  
43267
43268 Roo.form.ComboBoxArray = function(config)
43269 {
43270     this.addEvents({
43271         /**
43272          * @event beforeremove
43273          * Fires before remove the value from the list
43274              * @param {Roo.form.ComboBoxArray} _self This combo box array
43275              * @param {Roo.form.ComboBoxArray.Item} item removed item
43276              */
43277         'beforeremove' : true,
43278         /**
43279          * @event remove
43280          * Fires when remove the value from the list
43281              * @param {Roo.form.ComboBoxArray} _self This combo box array
43282              * @param {Roo.form.ComboBoxArray.Item} item removed item
43283              */
43284         'remove' : true
43285         
43286         
43287     });
43288     
43289     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43290     
43291     this.items = new Roo.util.MixedCollection(false);
43292     
43293     // construct the child combo...
43294     
43295     
43296     
43297     
43298    
43299     
43300 }
43301
43302  
43303 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43304
43305     /**
43306      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43307      */
43308     
43309     lastData : false,
43310     
43311     // behavies liek a hiddne field
43312     inputType:      'hidden',
43313     /**
43314      * @cfg {Number} width The width of the box that displays the selected element
43315      */ 
43316     width:          300,
43317
43318     
43319     
43320     /**
43321      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43322      */
43323     name : false,
43324     /**
43325      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43326      */
43327     hiddenName : false,
43328       /**
43329      * @cfg {String} seperator    The value seperator normally ',' 
43330      */
43331     seperator : ',',
43332     
43333     // private the array of items that are displayed..
43334     items  : false,
43335     // private - the hidden field el.
43336     hiddenEl : false,
43337     // private - the filed el..
43338     el : false,
43339     
43340     //validateValue : function() { return true; }, // all values are ok!
43341     //onAddClick: function() { },
43342     
43343     onRender : function(ct, position) 
43344     {
43345         
43346         // create the standard hidden element
43347         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43348         
43349         
43350         // give fake names to child combo;
43351         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43352         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43353         
43354         this.combo = Roo.factory(this.combo, Roo.form);
43355         this.combo.onRender(ct, position);
43356         if (typeof(this.combo.width) != 'undefined') {
43357             this.combo.onResize(this.combo.width,0);
43358         }
43359         
43360         this.combo.initEvents();
43361         
43362         // assigned so form know we need to do this..
43363         this.store          = this.combo.store;
43364         this.valueField     = this.combo.valueField;
43365         this.displayField   = this.combo.displayField ;
43366         
43367         
43368         this.combo.wrap.addClass('x-cbarray-grp');
43369         
43370         var cbwrap = this.combo.wrap.createChild(
43371             {tag: 'div', cls: 'x-cbarray-cb'},
43372             this.combo.el.dom
43373         );
43374         
43375              
43376         this.hiddenEl = this.combo.wrap.createChild({
43377             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43378         });
43379         this.el = this.combo.wrap.createChild({
43380             tag: 'input',  type:'hidden' , name: this.name, value : ''
43381         });
43382          //   this.el.dom.removeAttribute("name");
43383         
43384         
43385         this.outerWrap = this.combo.wrap;
43386         this.wrap = cbwrap;
43387         
43388         this.outerWrap.setWidth(this.width);
43389         this.outerWrap.dom.removeChild(this.el.dom);
43390         
43391         this.wrap.dom.appendChild(this.el.dom);
43392         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43393         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43394         
43395         this.combo.trigger.setStyle('position','relative');
43396         this.combo.trigger.setStyle('left', '0px');
43397         this.combo.trigger.setStyle('top', '2px');
43398         
43399         this.combo.el.setStyle('vertical-align', 'text-bottom');
43400         
43401         //this.trigger.setStyle('vertical-align', 'top');
43402         
43403         // this should use the code from combo really... on('add' ....)
43404         if (this.adder) {
43405             
43406         
43407             this.adder = this.outerWrap.createChild(
43408                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43409             var _t = this;
43410             this.adder.on('click', function(e) {
43411                 _t.fireEvent('adderclick', this, e);
43412             }, _t);
43413         }
43414         //var _t = this;
43415         //this.adder.on('click', this.onAddClick, _t);
43416         
43417         
43418         this.combo.on('select', function(cb, rec, ix) {
43419             this.addItem(rec.data);
43420             
43421             cb.setValue('');
43422             cb.el.dom.value = '';
43423             //cb.lastData = rec.data;
43424             // add to list
43425             
43426         }, this);
43427         
43428         
43429     },
43430     
43431     
43432     getName: function()
43433     {
43434         // returns hidden if it's set..
43435         if (!this.rendered) {return ''};
43436         return  this.hiddenName ? this.hiddenName : this.name;
43437         
43438     },
43439     
43440     
43441     onResize: function(w, h){
43442         
43443         return;
43444         // not sure if this is needed..
43445         //this.combo.onResize(w,h);
43446         
43447         if(typeof w != 'number'){
43448             // we do not handle it!?!?
43449             return;
43450         }
43451         var tw = this.combo.trigger.getWidth();
43452         tw += this.addicon ? this.addicon.getWidth() : 0;
43453         tw += this.editicon ? this.editicon.getWidth() : 0;
43454         var x = w - tw;
43455         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43456             
43457         this.combo.trigger.setStyle('left', '0px');
43458         
43459         if(this.list && this.listWidth === undefined){
43460             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43461             this.list.setWidth(lw);
43462             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43463         }
43464         
43465     
43466         
43467     },
43468     
43469     addItem: function(rec)
43470     {
43471         var valueField = this.combo.valueField;
43472         var displayField = this.combo.displayField;
43473         
43474         if (this.items.indexOfKey(rec[valueField]) > -1) {
43475             //console.log("GOT " + rec.data.id);
43476             return;
43477         }
43478         
43479         var x = new Roo.form.ComboBoxArray.Item({
43480             //id : rec[this.idField],
43481             data : rec,
43482             displayField : displayField ,
43483             tipField : displayField ,
43484             cb : this
43485         });
43486         // use the 
43487         this.items.add(rec[valueField],x);
43488         // add it before the element..
43489         this.updateHiddenEl();
43490         x.render(this.outerWrap, this.wrap.dom);
43491         // add the image handler..
43492     },
43493     
43494     updateHiddenEl : function()
43495     {
43496         this.validate();
43497         if (!this.hiddenEl) {
43498             return;
43499         }
43500         var ar = [];
43501         var idField = this.combo.valueField;
43502         
43503         this.items.each(function(f) {
43504             ar.push(f.data[idField]);
43505         });
43506         this.hiddenEl.dom.value = ar.join(this.seperator);
43507         this.validate();
43508     },
43509     
43510     reset : function()
43511     {
43512         this.items.clear();
43513         
43514         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43515            el.remove();
43516         });
43517         
43518         this.el.dom.value = '';
43519         if (this.hiddenEl) {
43520             this.hiddenEl.dom.value = '';
43521         }
43522         
43523     },
43524     getValue: function()
43525     {
43526         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43527     },
43528     setValue: function(v) // not a valid action - must use addItems..
43529     {
43530         
43531         this.reset();
43532          
43533         if (this.store.isLocal && (typeof(v) == 'string')) {
43534             // then we can use the store to find the values..
43535             // comma seperated at present.. this needs to allow JSON based encoding..
43536             this.hiddenEl.value  = v;
43537             var v_ar = [];
43538             Roo.each(v.split(this.seperator), function(k) {
43539                 Roo.log("CHECK " + this.valueField + ',' + k);
43540                 var li = this.store.query(this.valueField, k);
43541                 if (!li.length) {
43542                     return;
43543                 }
43544                 var add = {};
43545                 add[this.valueField] = k;
43546                 add[this.displayField] = li.item(0).data[this.displayField];
43547                 
43548                 this.addItem(add);
43549             }, this) 
43550              
43551         }
43552         if (typeof(v) == 'object' ) {
43553             // then let's assume it's an array of objects..
43554             Roo.each(v, function(l) {
43555                 var add = l;
43556                 if (typeof(l) == 'string') {
43557                     add = {};
43558                     add[this.valueField] = l;
43559                     add[this.displayField] = l
43560                 }
43561                 this.addItem(add);
43562             }, this);
43563              
43564         }
43565         
43566         
43567     },
43568     setFromData: function(v)
43569     {
43570         // this recieves an object, if setValues is called.
43571         this.reset();
43572         this.el.dom.value = v[this.displayField];
43573         this.hiddenEl.dom.value = v[this.valueField];
43574         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43575             return;
43576         }
43577         var kv = v[this.valueField];
43578         var dv = v[this.displayField];
43579         kv = typeof(kv) != 'string' ? '' : kv;
43580         dv = typeof(dv) != 'string' ? '' : dv;
43581         
43582         
43583         var keys = kv.split(this.seperator);
43584         var display = dv.split(this.seperator);
43585         for (var i = 0 ; i < keys.length; i++) {
43586             add = {};
43587             add[this.valueField] = keys[i];
43588             add[this.displayField] = display[i];
43589             this.addItem(add);
43590         }
43591       
43592         
43593     },
43594     
43595     /**
43596      * Validates the combox array value
43597      * @return {Boolean} True if the value is valid, else false
43598      */
43599     validate : function(){
43600         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43601             this.clearInvalid();
43602             return true;
43603         }
43604         return false;
43605     },
43606     
43607     validateValue : function(value){
43608         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43609         
43610     },
43611     
43612     /*@
43613      * overide
43614      * 
43615      */
43616     isDirty : function() {
43617         if(this.disabled) {
43618             return false;
43619         }
43620         
43621         try {
43622             var d = Roo.decode(String(this.originalValue));
43623         } catch (e) {
43624             return String(this.getValue()) !== String(this.originalValue);
43625         }
43626         
43627         var originalValue = [];
43628         
43629         for (var i = 0; i < d.length; i++){
43630             originalValue.push(d[i][this.valueField]);
43631         }
43632         
43633         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43634         
43635     }
43636     
43637 });
43638
43639
43640
43641 /**
43642  * @class Roo.form.ComboBoxArray.Item
43643  * @extends Roo.BoxComponent
43644  * A selected item in the list
43645  *  Fred [x]  Brian [x]  [Pick another |v]
43646  * 
43647  * @constructor
43648  * Create a new item.
43649  * @param {Object} config Configuration options
43650  */
43651  
43652 Roo.form.ComboBoxArray.Item = function(config) {
43653     config.id = Roo.id();
43654     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43655 }
43656
43657 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43658     data : {},
43659     cb: false,
43660     displayField : false,
43661     tipField : false,
43662     
43663     
43664     defaultAutoCreate : {
43665         tag: 'div',
43666         cls: 'x-cbarray-item',
43667         cn : [ 
43668             { tag: 'div' },
43669             {
43670                 tag: 'img',
43671                 width:16,
43672                 height : 16,
43673                 src : Roo.BLANK_IMAGE_URL ,
43674                 align: 'center'
43675             }
43676         ]
43677         
43678     },
43679     
43680  
43681     onRender : function(ct, position)
43682     {
43683         Roo.form.Field.superclass.onRender.call(this, ct, position);
43684         
43685         if(!this.el){
43686             var cfg = this.getAutoCreate();
43687             this.el = ct.createChild(cfg, position);
43688         }
43689         
43690         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43691         
43692         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43693             this.cb.renderer(this.data) :
43694             String.format('{0}',this.data[this.displayField]);
43695         
43696             
43697         this.el.child('div').dom.setAttribute('qtip',
43698                         String.format('{0}',this.data[this.tipField])
43699         );
43700         
43701         this.el.child('img').on('click', this.remove, this);
43702         
43703     },
43704    
43705     remove : function()
43706     {
43707         if(this.cb.disabled){
43708             return;
43709         }
43710         
43711         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43712             this.cb.items.remove(this);
43713             this.el.child('img').un('click', this.remove, this);
43714             this.el.remove();
43715             this.cb.updateHiddenEl();
43716
43717             this.cb.fireEvent('remove', this.cb, this);
43718         }
43719         
43720     }
43721 });/*
43722  * RooJS Library 1.1.1
43723  * Copyright(c) 2008-2011  Alan Knowles
43724  *
43725  * License - LGPL
43726  */
43727  
43728
43729 /**
43730  * @class Roo.form.ComboNested
43731  * @extends Roo.form.ComboBox
43732  * A combobox for that allows selection of nested items in a list,
43733  * eg.
43734  *
43735  *  Book
43736  *    -> red
43737  *    -> green
43738  *  Table
43739  *    -> square
43740  *      ->red
43741  *      ->green
43742  *    -> rectangle
43743  *      ->green
43744  *      
43745  * 
43746  * @constructor
43747  * Create a new ComboNested
43748  * @param {Object} config Configuration options
43749  */
43750 Roo.form.ComboNested = function(config){
43751     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43752     // should verify some data...
43753     // like
43754     // hiddenName = required..
43755     // displayField = required
43756     // valudField == required
43757     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43758     var _t = this;
43759     Roo.each(req, function(e) {
43760         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43761             throw "Roo.form.ComboNested : missing value for: " + e;
43762         }
43763     });
43764      
43765     
43766 };
43767
43768 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43769    
43770     /*
43771      * @config {Number} max Number of columns to show
43772      */
43773     
43774     maxColumns : 3,
43775    
43776     list : null, // the outermost div..
43777     innerLists : null, // the
43778     views : null,
43779     stores : null,
43780     // private
43781     loadingChildren : false,
43782     
43783     onRender : function(ct, position)
43784     {
43785         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43786         
43787         if(this.hiddenName){
43788             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43789                     'before', true);
43790             this.hiddenField.value =
43791                 this.hiddenValue !== undefined ? this.hiddenValue :
43792                 this.value !== undefined ? this.value : '';
43793
43794             // prevent input submission
43795             this.el.dom.removeAttribute('name');
43796              
43797              
43798         }
43799         
43800         if(Roo.isGecko){
43801             this.el.dom.setAttribute('autocomplete', 'off');
43802         }
43803
43804         var cls = 'x-combo-list';
43805
43806         this.list = new Roo.Layer({
43807             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43808         });
43809
43810         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43811         this.list.setWidth(lw);
43812         this.list.swallowEvent('mousewheel');
43813         this.assetHeight = 0;
43814
43815         if(this.title){
43816             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43817             this.assetHeight += this.header.getHeight();
43818         }
43819         this.innerLists = [];
43820         this.views = [];
43821         this.stores = [];
43822         for (var i =0 ; i < this.maxColumns; i++) {
43823             this.onRenderList( cls, i);
43824         }
43825         
43826         // always needs footer, as we are going to have an 'OK' button.
43827         this.footer = this.list.createChild({cls:cls+'-ft'});
43828         this.pageTb = new Roo.Toolbar(this.footer);  
43829         var _this = this;
43830         this.pageTb.add(  {
43831             
43832             text: 'Done',
43833             handler: function()
43834             {
43835                 _this.collapse();
43836             }
43837         });
43838         
43839         if ( this.allowBlank && !this.disableClear) {
43840             
43841             this.pageTb.add(new Roo.Toolbar.Fill(), {
43842                 cls: 'x-btn-icon x-btn-clear',
43843                 text: '&#160;',
43844                 handler: function()
43845                 {
43846                     _this.collapse();
43847                     _this.clearValue();
43848                     _this.onSelect(false, -1);
43849                 }
43850             });
43851         }
43852         if (this.footer) {
43853             this.assetHeight += this.footer.getHeight();
43854         }
43855         
43856     },
43857     onRenderList : function (  cls, i)
43858     {
43859         
43860         var lw = Math.floor(
43861                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43862         );
43863         
43864         this.list.setWidth(lw); // default to '1'
43865
43866         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43867         //il.on('mouseover', this.onViewOver, this, { list:  i });
43868         //il.on('mousemove', this.onViewMove, this, { list:  i });
43869         il.setWidth(lw);
43870         il.setStyle({ 'overflow-x' : 'hidden'});
43871
43872         if(!this.tpl){
43873             this.tpl = new Roo.Template({
43874                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43875                 isEmpty: function (value, allValues) {
43876                     //Roo.log(value);
43877                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43878                     return dl ? 'has-children' : 'no-children'
43879                 }
43880             });
43881         }
43882         
43883         var store  = this.store;
43884         if (i > 0) {
43885             store  = new Roo.data.SimpleStore({
43886                 //fields : this.store.reader.meta.fields,
43887                 reader : this.store.reader,
43888                 data : [ ]
43889             });
43890         }
43891         this.stores[i]  = store;
43892                   
43893         var view = this.views[i] = new Roo.View(
43894             il,
43895             this.tpl,
43896             {
43897                 singleSelect:true,
43898                 store: store,
43899                 selectedClass: this.selectedClass
43900             }
43901         );
43902         view.getEl().setWidth(lw);
43903         view.getEl().setStyle({
43904             position: i < 1 ? 'relative' : 'absolute',
43905             top: 0,
43906             left: (i * lw ) + 'px',
43907             display : i > 0 ? 'none' : 'block'
43908         });
43909         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43910         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43911         //view.on('click', this.onViewClick, this, { list : i });
43912
43913         store.on('beforeload', this.onBeforeLoad, this);
43914         store.on('load',  this.onLoad, this, { list  : i});
43915         store.on('loadexception', this.onLoadException, this);
43916
43917         // hide the other vies..
43918         
43919         
43920         
43921     },
43922       
43923     restrictHeight : function()
43924     {
43925         var mh = 0;
43926         Roo.each(this.innerLists, function(il,i) {
43927             var el = this.views[i].getEl();
43928             el.dom.style.height = '';
43929             var inner = el.dom;
43930             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43931             // only adjust heights on other ones..
43932             mh = Math.max(h, mh);
43933             if (i < 1) {
43934                 
43935                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43936                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43937                
43938             }
43939             
43940             
43941         }, this);
43942         
43943         this.list.beginUpdate();
43944         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43945         this.list.alignTo(this.el, this.listAlign);
43946         this.list.endUpdate();
43947         
43948     },
43949      
43950     
43951     // -- store handlers..
43952     // private
43953     onBeforeLoad : function()
43954     {
43955         if(!this.hasFocus){
43956             return;
43957         }
43958         this.innerLists[0].update(this.loadingText ?
43959                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43960         this.restrictHeight();
43961         this.selectedIndex = -1;
43962     },
43963     // private
43964     onLoad : function(a,b,c,d)
43965     {
43966         if (!this.loadingChildren) {
43967             // then we are loading the top level. - hide the children
43968             for (var i = 1;i < this.views.length; i++) {
43969                 this.views[i].getEl().setStyle({ display : 'none' });
43970             }
43971             var lw = Math.floor(
43972                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43973             );
43974         
43975              this.list.setWidth(lw); // default to '1'
43976
43977             
43978         }
43979         if(!this.hasFocus){
43980             return;
43981         }
43982         
43983         if(this.store.getCount() > 0) {
43984             this.expand();
43985             this.restrictHeight();   
43986         } else {
43987             this.onEmptyResults();
43988         }
43989         
43990         if (!this.loadingChildren) {
43991             this.selectActive();
43992         }
43993         /*
43994         this.stores[1].loadData([]);
43995         this.stores[2].loadData([]);
43996         this.views
43997         */    
43998     
43999         //this.el.focus();
44000     },
44001     
44002     
44003     // private
44004     onLoadException : function()
44005     {
44006         this.collapse();
44007         Roo.log(this.store.reader.jsonData);
44008         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44009             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44010         }
44011         
44012         
44013     },
44014     // no cleaning of leading spaces on blur here.
44015     cleanLeadingSpace : function(e) { },
44016     
44017
44018     onSelectChange : function (view, sels, opts )
44019     {
44020         var ix = view.getSelectedIndexes();
44021          
44022         if (opts.list > this.maxColumns - 2) {
44023             if (view.store.getCount()<  1) {
44024                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44025
44026             } else  {
44027                 if (ix.length) {
44028                     // used to clear ?? but if we are loading unselected 
44029                     this.setFromData(view.store.getAt(ix[0]).data);
44030                 }
44031                 
44032             }
44033             
44034             return;
44035         }
44036         
44037         if (!ix.length) {
44038             // this get's fired when trigger opens..
44039            // this.setFromData({});
44040             var str = this.stores[opts.list+1];
44041             str.data.clear(); // removeall wihtout the fire events..
44042             return;
44043         }
44044         
44045         var rec = view.store.getAt(ix[0]);
44046          
44047         this.setFromData(rec.data);
44048         this.fireEvent('select', this, rec, ix[0]);
44049         
44050         var lw = Math.floor(
44051              (
44052                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44053              ) / this.maxColumns
44054         );
44055         this.loadingChildren = true;
44056         this.stores[opts.list+1].loadDataFromChildren( rec );
44057         this.loadingChildren = false;
44058         var dl = this.stores[opts.list+1]. getTotalCount();
44059         
44060         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44061         
44062         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44063         for (var i = opts.list+2; i < this.views.length;i++) {
44064             this.views[i].getEl().setStyle({ display : 'none' });
44065         }
44066         
44067         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44068         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44069         
44070         if (this.isLoading) {
44071            // this.selectActive(opts.list);
44072         }
44073          
44074     },
44075     
44076     
44077     
44078     
44079     onDoubleClick : function()
44080     {
44081         this.collapse(); //??
44082     },
44083     
44084      
44085     
44086     
44087     
44088     // private
44089     recordToStack : function(store, prop, value, stack)
44090     {
44091         var cstore = new Roo.data.SimpleStore({
44092             //fields : this.store.reader.meta.fields, // we need array reader.. for
44093             reader : this.store.reader,
44094             data : [ ]
44095         });
44096         var _this = this;
44097         var record  = false;
44098         var srec = false;
44099         if(store.getCount() < 1){
44100             return false;
44101         }
44102         store.each(function(r){
44103             if(r.data[prop] == value){
44104                 record = r;
44105             srec = r;
44106                 return false;
44107             }
44108             if (r.data.cn && r.data.cn.length) {
44109                 cstore.loadDataFromChildren( r);
44110                 var cret = _this.recordToStack(cstore, prop, value, stack);
44111                 if (cret !== false) {
44112                     record = cret;
44113                     srec = r;
44114                     return false;
44115                 }
44116             }
44117              
44118             return true;
44119         });
44120         if (record == false) {
44121             return false
44122         }
44123         stack.unshift(srec);
44124         return record;
44125     },
44126     
44127     /*
44128      * find the stack of stores that match our value.
44129      *
44130      * 
44131      */
44132     
44133     selectActive : function ()
44134     {
44135         // if store is not loaded, then we will need to wait for that to happen first.
44136         var stack = [];
44137         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44138         for (var i = 0; i < stack.length; i++ ) {
44139             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44140         }
44141         
44142     }
44143         
44144          
44145     
44146     
44147     
44148     
44149 });/*
44150  * Based on:
44151  * Ext JS Library 1.1.1
44152  * Copyright(c) 2006-2007, Ext JS, LLC.
44153  *
44154  * Originally Released Under LGPL - original licence link has changed is not relivant.
44155  *
44156  * Fork - LGPL
44157  * <script type="text/javascript">
44158  */
44159 /**
44160  * @class Roo.form.Checkbox
44161  * @extends Roo.form.Field
44162  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44163  * @constructor
44164  * Creates a new Checkbox
44165  * @param {Object} config Configuration options
44166  */
44167 Roo.form.Checkbox = function(config){
44168     Roo.form.Checkbox.superclass.constructor.call(this, config);
44169     this.addEvents({
44170         /**
44171          * @event check
44172          * Fires when the checkbox is checked or unchecked.
44173              * @param {Roo.form.Checkbox} this This checkbox
44174              * @param {Boolean} checked The new checked value
44175              */
44176         check : true
44177     });
44178 };
44179
44180 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44181     /**
44182      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44183      */
44184     focusClass : undefined,
44185     /**
44186      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44187      */
44188     fieldClass: "x-form-field",
44189     /**
44190      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44191      */
44192     checked: false,
44193     /**
44194      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44195      * {tag: "input", type: "checkbox", autocomplete: "off"})
44196      */
44197     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44198     /**
44199      * @cfg {String} boxLabel The text that appears beside the checkbox
44200      */
44201     boxLabel : "",
44202     /**
44203      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44204      */  
44205     inputValue : '1',
44206     /**
44207      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44208      */
44209      valueOff: '0', // value when not checked..
44210
44211     actionMode : 'viewEl', 
44212     //
44213     // private
44214     itemCls : 'x-menu-check-item x-form-item',
44215     groupClass : 'x-menu-group-item',
44216     inputType : 'hidden',
44217     
44218     
44219     inSetChecked: false, // check that we are not calling self...
44220     
44221     inputElement: false, // real input element?
44222     basedOn: false, // ????
44223     
44224     isFormField: true, // not sure where this is needed!!!!
44225
44226     onResize : function(){
44227         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44228         if(!this.boxLabel){
44229             this.el.alignTo(this.wrap, 'c-c');
44230         }
44231     },
44232
44233     initEvents : function(){
44234         Roo.form.Checkbox.superclass.initEvents.call(this);
44235         this.el.on("click", this.onClick,  this);
44236         this.el.on("change", this.onClick,  this);
44237     },
44238
44239
44240     getResizeEl : function(){
44241         return this.wrap;
44242     },
44243
44244     getPositionEl : function(){
44245         return this.wrap;
44246     },
44247
44248     // private
44249     onRender : function(ct, position){
44250         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44251         /*
44252         if(this.inputValue !== undefined){
44253             this.el.dom.value = this.inputValue;
44254         }
44255         */
44256         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44257         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44258         var viewEl = this.wrap.createChild({ 
44259             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44260         this.viewEl = viewEl;   
44261         this.wrap.on('click', this.onClick,  this); 
44262         
44263         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44264         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44265         
44266         
44267         
44268         if(this.boxLabel){
44269             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44270         //    viewEl.on('click', this.onClick,  this); 
44271         }
44272         //if(this.checked){
44273             this.setChecked(this.checked);
44274         //}else{
44275             //this.checked = this.el.dom;
44276         //}
44277
44278     },
44279
44280     // private
44281     initValue : Roo.emptyFn,
44282
44283     /**
44284      * Returns the checked state of the checkbox.
44285      * @return {Boolean} True if checked, else false
44286      */
44287     getValue : function(){
44288         if(this.el){
44289             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44290         }
44291         return this.valueOff;
44292         
44293     },
44294
44295         // private
44296     onClick : function(){ 
44297         if (this.disabled) {
44298             return;
44299         }
44300         this.setChecked(!this.checked);
44301
44302         //if(this.el.dom.checked != this.checked){
44303         //    this.setValue(this.el.dom.checked);
44304        // }
44305     },
44306
44307     /**
44308      * Sets the checked state of the checkbox.
44309      * On is always based on a string comparison between inputValue and the param.
44310      * @param {Boolean/String} value - the value to set 
44311      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44312      */
44313     setValue : function(v,suppressEvent){
44314         
44315         
44316         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44317         //if(this.el && this.el.dom){
44318         //    this.el.dom.checked = this.checked;
44319         //    this.el.dom.defaultChecked = this.checked;
44320         //}
44321         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44322         //this.fireEvent("check", this, this.checked);
44323     },
44324     // private..
44325     setChecked : function(state,suppressEvent)
44326     {
44327         if (this.inSetChecked) {
44328             this.checked = state;
44329             return;
44330         }
44331         
44332     
44333         if(this.wrap){
44334             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44335         }
44336         this.checked = state;
44337         if(suppressEvent !== true){
44338             this.fireEvent('check', this, state);
44339         }
44340         this.inSetChecked = true;
44341         this.el.dom.value = state ? this.inputValue : this.valueOff;
44342         this.inSetChecked = false;
44343         
44344     },
44345     // handle setting of hidden value by some other method!!?!?
44346     setFromHidden: function()
44347     {
44348         if(!this.el){
44349             return;
44350         }
44351         //console.log("SET FROM HIDDEN");
44352         //alert('setFrom hidden');
44353         this.setValue(this.el.dom.value);
44354     },
44355     
44356     onDestroy : function()
44357     {
44358         if(this.viewEl){
44359             Roo.get(this.viewEl).remove();
44360         }
44361          
44362         Roo.form.Checkbox.superclass.onDestroy.call(this);
44363     },
44364     
44365     setBoxLabel : function(str)
44366     {
44367         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44368     }
44369
44370 });/*
44371  * Based on:
44372  * Ext JS Library 1.1.1
44373  * Copyright(c) 2006-2007, Ext JS, LLC.
44374  *
44375  * Originally Released Under LGPL - original licence link has changed is not relivant.
44376  *
44377  * Fork - LGPL
44378  * <script type="text/javascript">
44379  */
44380  
44381 /**
44382  * @class Roo.form.Radio
44383  * @extends Roo.form.Checkbox
44384  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44385  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44386  * @constructor
44387  * Creates a new Radio
44388  * @param {Object} config Configuration options
44389  */
44390 Roo.form.Radio = function(){
44391     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44392 };
44393 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44394     inputType: 'radio',
44395
44396     /**
44397      * If this radio is part of a group, it will return the selected value
44398      * @return {String}
44399      */
44400     getGroupValue : function(){
44401         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44402     },
44403     
44404     
44405     onRender : function(ct, position){
44406         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44407         
44408         if(this.inputValue !== undefined){
44409             this.el.dom.value = this.inputValue;
44410         }
44411          
44412         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44413         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44414         //var viewEl = this.wrap.createChild({ 
44415         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44416         //this.viewEl = viewEl;   
44417         //this.wrap.on('click', this.onClick,  this); 
44418         
44419         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44420         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44421         
44422         
44423         
44424         if(this.boxLabel){
44425             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44426         //    viewEl.on('click', this.onClick,  this); 
44427         }
44428          if(this.checked){
44429             this.el.dom.checked =   'checked' ;
44430         }
44431          
44432     } 
44433     
44434     
44435 });Roo.htmleditor = {}; 
44436 /**
44437  * @class Roo.htmleditor.Filter
44438  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
44439  * @cfg {DomElement} node The node to iterate and filter
44440  * @cfg {boolean|String|Array} tag Tags to replace 
44441  * @constructor
44442  * Create a new Filter.
44443  * @param {Object} config Configuration options
44444  */
44445
44446
44447
44448 Roo.htmleditor.Filter = function(cfg) {
44449     Roo.apply(this.cfg);
44450     // this does not actually call walk as it's really just a abstract class
44451 }
44452
44453
44454 Roo.htmleditor.Filter.prototype = {
44455     
44456     node: false,
44457     
44458     tag: false,
44459
44460     // overrride to do replace comments.
44461     replaceComment : false,
44462     
44463     // overrride to do replace or do stuff with tags..
44464     replaceTag : false,
44465     
44466     walk : function(dom)
44467     {
44468         Roo.each( Array.from(dom.childNodes), function( e ) {
44469             switch(true) {
44470                 
44471                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
44472                     this.replaceComment(e);
44473                     return;
44474                 
44475                 case e.nodeType != 1: //not a node.
44476                     return;
44477                 
44478                 case this.tag === true: // everything
44479                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
44480                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
44481                     if (this.replaceTag && false === this.replaceTag(e)) {
44482                         return;
44483                     }
44484                     if (e.hasChildNodes()) {
44485                         this.walk(e);
44486                     }
44487                     return;
44488                 
44489                 default:    // tags .. that do not match.
44490                     if (e.hasChildNodes()) {
44491                         this.walk(e);
44492                     }
44493             }
44494             
44495         }, this);
44496         
44497     }
44498 }; 
44499
44500 /**
44501  * @class Roo.htmleditor.FilterAttributes
44502  * clean attributes and  styles including http:// etc.. in attribute
44503  * @constructor
44504 * Run a new Attribute Filter
44505 * @param {Object} config Configuration options
44506  */
44507 Roo.htmleditor.FilterAttributes = function(cfg)
44508 {
44509     Roo.apply(this, cfg);
44510     this.attrib_black = this.attrib_black || [];
44511     this.attrib_clean = this.attrib_clean || [];
44512     this.style_white = this.style_white || [];
44513     this.style_black = this.style_black || [];
44514     this.walk(cfg.node);
44515 }
44516
44517 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
44518 {
44519     tag: true, // all tags
44520     
44521     attrib_black : false, // array
44522     attrib_clean : false,
44523     style_white : false,
44524     style_black : false,
44525      
44526      
44527     replaceTag : function(node)
44528     {
44529         if (!node.attributes || !node.attributes.length) {
44530             return true;
44531         }
44532         
44533         for (var i = node.attributes.length-1; i > -1 ; i--) {
44534             var a = node.attributes[i];
44535             //console.log(a);
44536             
44537             if (a.name.toLowerCase().substr(0,2)=='on')  {
44538                 node.removeAttribute(a.name);
44539                 continue;
44540             }
44541             
44542             
44543             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
44544                 node.removeAttribute(a.name);
44545                 continue;
44546             }
44547             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
44548                 this.cleanAttr(node,a.name,a.value); // fixme..
44549                 continue;
44550             }
44551             if (a.name == 'style') {
44552                 this.cleanStyle(node,a.name,a.value);
44553                 continue;
44554             }
44555             /// clean up MS crap..
44556             // tecnically this should be a list of valid class'es..
44557             
44558             
44559             if (a.name == 'class') {
44560                 if (a.value.match(/^Mso/)) {
44561                     node.removeAttribute('class');
44562                 }
44563                 
44564                 if (a.value.match(/^body$/)) {
44565                     node.removeAttribute('class');
44566                 }
44567                 continue;
44568             }
44569             
44570             
44571             // style cleanup!?
44572             // class cleanup?
44573             
44574         }
44575         return true; // clean children
44576     },
44577         
44578     cleanAttr: function(node, n,v)
44579     {
44580         
44581         if (v.match(/^\./) || v.match(/^\//)) {
44582             return;
44583         }
44584         if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44585             return;
44586         }
44587         if (v.match(/^#/)) {
44588             return;
44589         }
44590         if (v.match(/^\{/)) { // allow template editing.
44591             return;
44592         }
44593 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44594         node.removeAttribute(n);
44595         
44596     },
44597     cleanStyle : function(node,  n,v)
44598     {
44599         if (v.match(/expression/)) { //XSS?? should we even bother..
44600             node.removeAttribute(n);
44601             return;
44602         }
44603         
44604         var parts = v.split(/;/);
44605         var clean = [];
44606         
44607         Roo.each(parts, function(p) {
44608             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44609             if (!p.length) {
44610                 return true;
44611             }
44612             var l = p.split(':').shift().replace(/\s+/g,'');
44613             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44614             
44615             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
44616                 return true;
44617             }
44618             //Roo.log()
44619             // only allow 'c whitelisted system attributes'
44620             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
44621                 return true;
44622             }
44623             
44624             
44625             clean.push(p);
44626             return true;
44627         },this);
44628         if (clean.length) { 
44629             node.setAttribute(n, clean.join(';'));
44630         } else {
44631             node.removeAttribute(n);
44632         }
44633         
44634     }
44635         
44636         
44637         
44638     
44639 });/**
44640  * @class Roo.htmleditor.FilterBlack
44641  * remove blacklisted elements.
44642  * @constructor
44643  * Run a new Blacklisted Filter
44644  * @param {Object} config Configuration options
44645  */
44646
44647 Roo.htmleditor.FilterBlack = function(cfg)
44648 {
44649     Roo.apply(this, cfg);
44650     this.walk(cfg.node);
44651 }
44652
44653 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
44654 {
44655     tag : true, // all elements.
44656    
44657     replace : function(n)
44658     {
44659         n.parentNode.removeChild(n);
44660     }
44661 });
44662 /**
44663  * @class Roo.htmleditor.FilterComment
44664  * remove comments.
44665  * @constructor
44666 * Run a new Comments Filter
44667 * @param {Object} config Configuration options
44668  */
44669 Roo.htmleditor.FilterComment = function(cfg)
44670 {
44671     this.walk(cfg.node);
44672 }
44673
44674 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
44675 {
44676   
44677     replaceComment : function(n)
44678     {
44679         n.parentNode.removeChild(n);
44680     }
44681 });/**
44682  * @class Roo.htmleditor.FilterKeepChildren
44683  * remove tags but keep children
44684  * @constructor
44685  * Run a new Keep Children Filter
44686  * @param {Object} config Configuration options
44687  */
44688
44689 Roo.htmleditor.FilterKeepChildren = function(cfg)
44690 {
44691     Roo.apply(this, cfg);
44692     this.walk(cfg.node);
44693 }
44694
44695 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
44696 {
44697     
44698   
44699     replaceTag : function(node)
44700     {
44701         // walk children...
44702         var ar = Array.from(node.childNodes);
44703         for (var i = 0; i < ar.length; i++) {
44704             node.removeChild(ar[i]);
44705             // what if we need to walk these???
44706             node.parentNode.insertBefore(ar[i], node);
44707             this.walk(ar[i]);
44708         }
44709         node.parentNode.removeChild(node);
44710         return false; // don't walk children
44711         
44712         
44713     }
44714 });/**
44715  * @class Roo.htmleditor.FilterParagraph
44716  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
44717  * like on 'push' to remove the <p> tags and replace them with line breaks.
44718  * @constructor
44719  * Run a new Paragraph Filter
44720  * @param {Object} config Configuration options
44721  */
44722
44723 Roo.htmleditor.FilterParagraph = function(cfg)
44724 {
44725     // no need to apply config.
44726     this.walk(cfg.node);
44727 }
44728
44729 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
44730 {
44731     
44732      
44733     tag : 'P',
44734     
44735      
44736     replaceTag : function(node)
44737     {
44738         
44739         if (node.childNodes.length == 1 &&
44740             node.childNodes[0].nodeType == 3 &&
44741             node.childNodes[0].textContent.trim().length < 1
44742             ) {
44743             // remove and replace with '<BR>';
44744             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
44745             return false; // no need to walk..
44746         }
44747         var ar = Array.from(node.childNodes);
44748         for (var i = 0; i < ar.length; i++) {
44749             node.removeChild(ar[i]);
44750             // what if we need to walk these???
44751             node.parentNode.insertBefore(ar[i], node);
44752         }
44753         // now what about this?
44754         // <p> &nbsp; </p>
44755         
44756         // double BR.
44757         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
44758         node.parentNode.removeChild(node);
44759         
44760         return false;
44761
44762     }
44763     
44764 });/**
44765  * @class Roo.htmleditor.FilterSpan
44766  * filter span's with no attributes out..
44767  * @constructor
44768  * Run a new Span Filter
44769  * @param {Object} config Configuration options
44770  */
44771
44772 Roo.htmleditor.FilterSpan = function(cfg)
44773 {
44774     // no need to apply config.
44775     this.walk(cfg.node);
44776 }
44777
44778 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
44779 {
44780      
44781     tag : 'SPAN',
44782      
44783  
44784     replaceTag : function(node)
44785     {
44786         if (node.attributes && node.attributes.length > 0) {
44787             return true; // walk if there are any.
44788         }
44789         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
44790         return false;
44791      
44792     }
44793     
44794 });/**
44795  * @class Roo.htmleditor.FilterTableWidth
44796   try and remove table width data - as that frequently messes up other stuff.
44797  * 
44798  *      was cleanTableWidths.
44799  *
44800  * Quite often pasting from word etc.. results in tables with column and widths.
44801  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44802  *
44803  * @constructor
44804  * Run a new Table Filter
44805  * @param {Object} config Configuration options
44806  */
44807
44808 Roo.htmleditor.FilterTableWidth = function(cfg)
44809 {
44810     // no need to apply config.
44811     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
44812     this.walk(cfg.node);
44813 }
44814
44815 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
44816 {
44817      
44818      
44819     
44820     replaceTag: function(node) {
44821         
44822         
44823       
44824         if (node.hasAttribute('width')) {
44825             node.removeAttribute('width');
44826         }
44827         
44828          
44829         if (node.hasAttribute("style")) {
44830             // pretty basic...
44831             
44832             var styles = node.getAttribute("style").split(";");
44833             var nstyle = [];
44834             Roo.each(styles, function(s) {
44835                 if (!s.match(/:/)) {
44836                     return;
44837                 }
44838                 var kv = s.split(":");
44839                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44840                     return;
44841                 }
44842                 // what ever is left... we allow.
44843                 nstyle.push(s);
44844             });
44845             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44846             if (!nstyle.length) {
44847                 node.removeAttribute('style');
44848             }
44849         }
44850         
44851         return true; // continue doing children..
44852     }
44853 });/**
44854  * @class Roo.htmleditor.FilterWord
44855  * try and clean up all the mess that Word generates.
44856  * 
44857  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
44858  
44859  * @constructor
44860  * Run a new Span Filter
44861  * @param {Object} config Configuration options
44862  */
44863
44864 Roo.htmleditor.FilterWord = function(cfg)
44865 {
44866     // no need to apply config.
44867     this.walk(cfg.node);
44868 }
44869
44870 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
44871 {
44872     tag: true,
44873      
44874     
44875     /**
44876      * Clean up MS wordisms...
44877      */
44878     replaceTag : function(node)
44879     {
44880          
44881         // no idea what this does - span with text, replaceds with just text.
44882         if(
44883                 node.nodeName == 'SPAN' &&
44884                 !node.hasAttributes() &&
44885                 node.childNodes.length == 1 &&
44886                 node.firstChild.nodeName == "#text"  
44887         ) {
44888             var textNode = node.firstChild;
44889             node.removeChild(textNode);
44890             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44891                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44892             }
44893             node.parentNode.insertBefore(textNode, node);
44894             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44895                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44896             }
44897             
44898             node.parentNode.removeChild(node);
44899             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
44900         }
44901         
44902    
44903         
44904         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44905             node.parentNode.removeChild(node);
44906             return false; // dont do chidlren
44907         }
44908         //Roo.log(node.tagName);
44909         // remove - but keep children..
44910         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44911             //Roo.log('-- removed');
44912             while (node.childNodes.length) {
44913                 var cn = node.childNodes[0];
44914                 node.removeChild(cn);
44915                 node.parentNode.insertBefore(cn, node);
44916                 // move node to parent - and clean it..
44917                 this.replaceTag(cn);
44918             }
44919             node.parentNode.removeChild(node);
44920             /// no need to iterate chidlren = it's got none..
44921             //this.iterateChildren(node, this.cleanWord);
44922             return false; // no need to iterate children.
44923         }
44924         // clean styles
44925         if (node.className.length) {
44926             
44927             var cn = node.className.split(/\W+/);
44928             var cna = [];
44929             Roo.each(cn, function(cls) {
44930                 if (cls.match(/Mso[a-zA-Z]+/)) {
44931                     return;
44932                 }
44933                 cna.push(cls);
44934             });
44935             node.className = cna.length ? cna.join(' ') : '';
44936             if (!cna.length) {
44937                 node.removeAttribute("class");
44938             }
44939         }
44940         
44941         if (node.hasAttribute("lang")) {
44942             node.removeAttribute("lang");
44943         }
44944         
44945         if (node.hasAttribute("style")) {
44946             
44947             var styles = node.getAttribute("style").split(";");
44948             var nstyle = [];
44949             Roo.each(styles, function(s) {
44950                 if (!s.match(/:/)) {
44951                     return;
44952                 }
44953                 var kv = s.split(":");
44954                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44955                     return;
44956                 }
44957                 // what ever is left... we allow.
44958                 nstyle.push(s);
44959             });
44960             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44961             if (!nstyle.length) {
44962                 node.removeAttribute('style');
44963             }
44964         }
44965         return true; // do children
44966         
44967         
44968         
44969     }
44970 });
44971 /**
44972  * @class Roo.htmleditor.Tidy
44973  * Tidy HTML 
44974  * @cfg {Roo.HtmlEditorCore} core the editor.
44975  * @constructor
44976  * Create a new Filter.
44977  * @param {Object} config Configuration options
44978  */
44979
44980
44981 Roo.htmleditor.Tidy = function(cfg) {
44982     Roo.apply(this, cfg);
44983     
44984     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
44985      
44986 }
44987
44988 Roo.htmleditor.Tidy.toString = function(node)
44989 {
44990     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
44991 }
44992
44993 Roo.htmleditor.Tidy.prototype = {
44994     
44995     
44996     wrap : function(s) {
44997         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
44998     },
44999
45000     
45001     tidy : function(node, indent) {
45002      
45003         if  (node.nodeType == 3) {
45004             // text.
45005             
45006             
45007             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45008                 
45009             
45010         }
45011         
45012         if  (node.nodeType != 1) {
45013             return '';
45014         }
45015         
45016         
45017         
45018         if (node.tagName == 'BODY') {
45019             
45020             return this.cn(node, '');
45021         }
45022              
45023              // Prints the node tagName, such as <A>, <IMG>, etc
45024         var ret = "<" + node.tagName +  this.attr(node) ;
45025         
45026         // elements with no children..
45027         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45028                 return ret + '/>';
45029         }
45030         ret += '>';
45031         
45032         
45033         var cindent = indent === false ? '' : (indent + '  ');
45034         // tags where we will not pad the children.. (inline text tags etc..)
45035         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45036             cindent = false;
45037             
45038             
45039         }
45040         
45041         var cn = this.cn(node, cindent );
45042         
45043         return ret + cn  + '</' + node.tagName + '>';
45044         
45045     },
45046     cn: function(node, indent)
45047     {
45048         var ret = [];
45049         
45050         var ar = Array.from(node.childNodes);
45051         for (var i = 0 ; i < ar.length ; i++) {
45052             if (indent !== false   // indent==false preservies everything
45053                 && i > 0
45054                 && ar[i].nodeType == 3 
45055                 && ar[i].nodeValue.length > 0
45056                 && ar[i].nodeValue.match(/^\s+/)
45057             ) {
45058                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45059             }
45060             if (indent !== false
45061                 && ar[i].nodeType == 1 // element - and indent is not set... 
45062             ) {
45063                 ret.push("\n" + indent); 
45064             }
45065             
45066             ret.push(this.tidy(ar[i], indent));
45067             // text + trailing indent 
45068             if (indent !== false
45069                 && ar[i].nodeType == 3
45070                 && ar[i].nodeValue.length > 0
45071                 && ar[i].nodeValue.match(/\s+$/)
45072             ){
45073                 ret.push("\n" + indent); 
45074             }
45075             
45076             
45077             
45078             
45079         }
45080         // what if all text?
45081         
45082         
45083         return ret.join('');
45084     },
45085     
45086          
45087         
45088     attr : function(node)
45089     {
45090         var attr = [];
45091         for(i = 0; i < node.attributes.length;i++) {
45092             
45093             // skip empty values?
45094             if (!node.attributes.item(i).value.length) {
45095                 continue;
45096             }
45097             attr.push(  node.attributes.item(i).name + '="' +
45098                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45099             );
45100         }
45101         return attr.length ? (' ' + attr.join(' ') ) : '';
45102         
45103     }
45104     
45105     
45106     
45107 }
45108 /**
45109  * @class Roo.htmleditor.KeyEnter
45110  * Handle Enter press..
45111  * @cfg {Roo.HtmlEditorCore} core the editor.
45112  * @constructor
45113  * Create a new Filter.
45114  * @param {Object} config Configuration options
45115  */
45116
45117
45118
45119 Roo.htmleditor.KeyEnter = function(cfg) {
45120     Roo.apply(this, cfg);
45121     // this does not actually call walk as it's really just a abstract class
45122  
45123     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45124 }
45125
45126
45127 Roo.htmleditor.KeyEnter.prototype = {
45128     
45129     core : false,
45130     
45131     keypress : function(e) {
45132         if (e.charCode != 13) {
45133             return true;
45134         }
45135         e.preventDefault();
45136         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45137         var doc = this.core.doc;
45138         
45139         var docFragment = doc.createDocumentFragment();
45140     
45141         //add a new line
45142         var newEle = doc.createTextNode('\n');
45143         docFragment.appendChild(newEle);
45144     
45145         //add the br, or p, or something else
45146         newEle = doc.createElement('br');
45147         docFragment.appendChild(newEle);
45148     
45149         //make the br replace selection
45150         var range = this.core.win.getSelection().getRangeAt(0);
45151         range.deleteContents();
45152         range.insertNode(docFragment);
45153     
45154         //create a new range
45155         range = doc.createRange();
45156         range.setStartAfter(newEle);
45157         range.collapse(true);
45158     
45159         //make the cursor there
45160         var sel = this.core.win.getSelection();
45161         sel.removeAllRanges();
45162         sel.addRange(range);
45163     
45164         return false;
45165          
45166     }
45167 };
45168     //<script type="text/javascript">
45169
45170 /*
45171  * Based  Ext JS Library 1.1.1
45172  * Copyright(c) 2006-2007, Ext JS, LLC.
45173  * LGPL
45174  *
45175  */
45176  
45177 /**
45178  * @class Roo.HtmlEditorCore
45179  * @extends Roo.Component
45180  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
45181  *
45182  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45183  */
45184
45185 Roo.HtmlEditorCore = function(config){
45186     
45187     
45188     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
45189     
45190     
45191     this.addEvents({
45192         /**
45193          * @event initialize
45194          * Fires when the editor is fully initialized (including the iframe)
45195          * @param {Roo.HtmlEditorCore} this
45196          */
45197         initialize: true,
45198         /**
45199          * @event activate
45200          * Fires when the editor is first receives the focus. Any insertion must wait
45201          * until after this event.
45202          * @param {Roo.HtmlEditorCore} this
45203          */
45204         activate: true,
45205          /**
45206          * @event beforesync
45207          * Fires before the textarea is updated with content from the editor iframe. Return false
45208          * to cancel the sync.
45209          * @param {Roo.HtmlEditorCore} this
45210          * @param {String} html
45211          */
45212         beforesync: true,
45213          /**
45214          * @event beforepush
45215          * Fires before the iframe editor is updated with content from the textarea. Return false
45216          * to cancel the push.
45217          * @param {Roo.HtmlEditorCore} this
45218          * @param {String} html
45219          */
45220         beforepush: true,
45221          /**
45222          * @event sync
45223          * Fires when the textarea is updated with content from the editor iframe.
45224          * @param {Roo.HtmlEditorCore} this
45225          * @param {String} html
45226          */
45227         sync: true,
45228          /**
45229          * @event push
45230          * Fires when the iframe editor is updated with content from the textarea.
45231          * @param {Roo.HtmlEditorCore} this
45232          * @param {String} html
45233          */
45234         push: true,
45235         
45236         /**
45237          * @event editorevent
45238          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45239          * @param {Roo.HtmlEditorCore} this
45240          */
45241         editorevent: true
45242         
45243     });
45244     
45245     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
45246     
45247     // defaults : white / black...
45248     this.applyBlacklists();
45249     
45250     
45251     
45252 };
45253
45254
45255 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
45256
45257
45258      /**
45259      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
45260      */
45261     
45262     owner : false,
45263     
45264      /**
45265      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45266      *                        Roo.resizable.
45267      */
45268     resizable : false,
45269      /**
45270      * @cfg {Number} height (in pixels)
45271      */   
45272     height: 300,
45273    /**
45274      * @cfg {Number} width (in pixels)
45275      */   
45276     width: 500,
45277     
45278     /**
45279      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45280      * 
45281      */
45282     stylesheets: false,
45283     
45284     /**
45285      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45286      */
45287     allowComments: false,
45288     // id of frame..
45289     frameId: false,
45290     
45291     // private properties
45292     validationEvent : false,
45293     deferHeight: true,
45294     initialized : false,
45295     activated : false,
45296     sourceEditMode : false,
45297     onFocus : Roo.emptyFn,
45298     iframePad:3,
45299     hideMode:'offsets',
45300     
45301     clearUp: true,
45302     
45303     // blacklist + whitelisted elements..
45304     black: false,
45305     white: false,
45306      
45307     bodyCls : '',
45308
45309     /**
45310      * Protected method that will not generally be called directly. It
45311      * is called when the editor initializes the iframe with HTML contents. Override this method if you
45312      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
45313      */
45314     getDocMarkup : function(){
45315         // body styles..
45316         var st = '';
45317         
45318         // inherit styels from page...?? 
45319         if (this.stylesheets === false) {
45320             
45321             Roo.get(document.head).select('style').each(function(node) {
45322                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45323             });
45324             
45325             Roo.get(document.head).select('link').each(function(node) { 
45326                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45327             });
45328             
45329         } else if (!this.stylesheets.length) {
45330                 // simple..
45331                 st = '<style type="text/css">' +
45332                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45333                    '</style>';
45334         } else {
45335             for (var i in this.stylesheets) {
45336                 if (typeof(this.stylesheets[i]) != 'string') {
45337                     continue;
45338                 }
45339                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
45340             }
45341             
45342         }
45343         
45344         st +=  '<style type="text/css">' +
45345             'IMG { cursor: pointer } ' +
45346         '</style>';
45347
45348         var cls = 'roo-htmleditor-body';
45349         
45350         if(this.bodyCls.length){
45351             cls += ' ' + this.bodyCls;
45352         }
45353         
45354         return '<html><head>' + st  +
45355             //<style type="text/css">' +
45356             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45357             //'</style>' +
45358             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
45359     },
45360
45361     // private
45362     onRender : function(ct, position)
45363     {
45364         var _t = this;
45365         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
45366         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
45367         
45368         
45369         this.el.dom.style.border = '0 none';
45370         this.el.dom.setAttribute('tabIndex', -1);
45371         this.el.addClass('x-hidden hide');
45372         
45373         
45374         
45375         if(Roo.isIE){ // fix IE 1px bogus margin
45376             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
45377         }
45378        
45379         
45380         this.frameId = Roo.id();
45381         
45382          
45383         
45384         var iframe = this.owner.wrap.createChild({
45385             tag: 'iframe',
45386             cls: 'form-control', // bootstrap..
45387             id: this.frameId,
45388             name: this.frameId,
45389             frameBorder : 'no',
45390             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
45391         }, this.el
45392         );
45393         
45394         
45395         this.iframe = iframe.dom;
45396
45397          this.assignDocWin();
45398         
45399         this.doc.designMode = 'on';
45400        
45401         this.doc.open();
45402         this.doc.write(this.getDocMarkup());
45403         this.doc.close();
45404
45405         
45406         var task = { // must defer to wait for browser to be ready
45407             run : function(){
45408                 //console.log("run task?" + this.doc.readyState);
45409                 this.assignDocWin();
45410                 if(this.doc.body || this.doc.readyState == 'complete'){
45411                     try {
45412                         this.doc.designMode="on";
45413                     } catch (e) {
45414                         return;
45415                     }
45416                     Roo.TaskMgr.stop(task);
45417                     this.initEditor.defer(10, this);
45418                 }
45419             },
45420             interval : 10,
45421             duration: 10000,
45422             scope: this
45423         };
45424         Roo.TaskMgr.start(task);
45425
45426     },
45427
45428     // private
45429     onResize : function(w, h)
45430     {
45431          Roo.log('resize: ' +w + ',' + h );
45432         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
45433         if(!this.iframe){
45434             return;
45435         }
45436         if(typeof w == 'number'){
45437             
45438             this.iframe.style.width = w + 'px';
45439         }
45440         if(typeof h == 'number'){
45441             
45442             this.iframe.style.height = h + 'px';
45443             if(this.doc){
45444                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
45445             }
45446         }
45447         
45448     },
45449
45450     /**
45451      * Toggles the editor between standard and source edit mode.
45452      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45453      */
45454     toggleSourceEdit : function(sourceEditMode){
45455         
45456         this.sourceEditMode = sourceEditMode === true;
45457         
45458         if(this.sourceEditMode){
45459  
45460             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
45461             
45462         }else{
45463             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
45464             //this.iframe.className = '';
45465             this.deferFocus();
45466         }
45467         //this.setSize(this.owner.wrap.getSize());
45468         //this.fireEvent('editmodechange', this, this.sourceEditMode);
45469     },
45470
45471     
45472   
45473
45474     /**
45475      * Protected method that will not generally be called directly. If you need/want
45476      * custom HTML cleanup, this is the method you should override.
45477      * @param {String} html The HTML to be cleaned
45478      * return {String} The cleaned HTML
45479      */
45480     cleanHtml : function(html){
45481         html = String(html);
45482         if(html.length > 5){
45483             if(Roo.isSafari){ // strip safari nonsense
45484                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
45485             }
45486         }
45487         if(html == '&nbsp;'){
45488             html = '';
45489         }
45490         return html;
45491     },
45492
45493     /**
45494      * HTML Editor -> Textarea
45495      * Protected method that will not generally be called directly. Syncs the contents
45496      * of the editor iframe with the textarea.
45497      */
45498     syncValue : function(){
45499         if(this.initialized){
45500             var bd = (this.doc.body || this.doc.documentElement);
45501             //this.cleanUpPaste(); -- this is done else where and causes havoc..
45502             var html = bd.innerHTML;
45503             if(Roo.isSafari){
45504                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
45505                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
45506                 if(m && m[1]){
45507                     html = '<div style="'+m[0]+'">' + html + '</div>';
45508                 }
45509             }
45510             html = this.cleanHtml(html);
45511             // fix up the special chars.. normaly like back quotes in word...
45512             // however we do not want to do this with chinese..
45513             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
45514                 
45515                 var cc = match.charCodeAt();
45516
45517                 // Get the character value, handling surrogate pairs
45518                 if (match.length == 2) {
45519                     // It's a surrogate pair, calculate the Unicode code point
45520                     var high = match.charCodeAt(0) - 0xD800;
45521                     var low  = match.charCodeAt(1) - 0xDC00;
45522                     cc = (high * 0x400) + low + 0x10000;
45523                 }  else if (
45524                     (cc >= 0x4E00 && cc < 0xA000 ) ||
45525                     (cc >= 0x3400 && cc < 0x4E00 ) ||
45526                     (cc >= 0xf900 && cc < 0xfb00 )
45527                 ) {
45528                         return match;
45529                 }  
45530          
45531                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
45532                 return "&#" + cc + ";";
45533                 
45534                 
45535             });
45536             
45537             
45538              
45539             if(this.owner.fireEvent('beforesync', this, html) !== false){
45540                 this.el.dom.value = html;
45541                 this.owner.fireEvent('sync', this, html);
45542             }
45543         }
45544     },
45545
45546     /**
45547      * Protected method that will not generally be called directly. Pushes the value of the textarea
45548      * into the iframe editor.
45549      */
45550     pushValue : function(){
45551         if(this.initialized){
45552             var v = this.el.dom.value.trim();
45553             
45554 //            if(v.length < 1){
45555 //                v = '&#160;';
45556 //            }
45557             
45558             if(this.owner.fireEvent('beforepush', this, v) !== false){
45559                 var d = (this.doc.body || this.doc.documentElement);
45560                 d.innerHTML = v;
45561                 this.cleanUpPaste();
45562                 this.el.dom.value = d.innerHTML;
45563                 this.owner.fireEvent('push', this, v);
45564             }
45565         }
45566     },
45567
45568     // private
45569     deferFocus : function(){
45570         this.focus.defer(10, this);
45571     },
45572
45573     // doc'ed in Field
45574     focus : function(){
45575         if(this.win && !this.sourceEditMode){
45576             this.win.focus();
45577         }else{
45578             this.el.focus();
45579         }
45580     },
45581     
45582     assignDocWin: function()
45583     {
45584         var iframe = this.iframe;
45585         
45586          if(Roo.isIE){
45587             this.doc = iframe.contentWindow.document;
45588             this.win = iframe.contentWindow;
45589         } else {
45590 //            if (!Roo.get(this.frameId)) {
45591 //                return;
45592 //            }
45593 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45594 //            this.win = Roo.get(this.frameId).dom.contentWindow;
45595             
45596             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
45597                 return;
45598             }
45599             
45600             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45601             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
45602         }
45603     },
45604     
45605     // private
45606     initEditor : function(){
45607         //console.log("INIT EDITOR");
45608         this.assignDocWin();
45609         
45610         
45611         
45612         this.doc.designMode="on";
45613         this.doc.open();
45614         this.doc.write(this.getDocMarkup());
45615         this.doc.close();
45616         
45617         var dbody = (this.doc.body || this.doc.documentElement);
45618         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
45619         // this copies styles from the containing element into thsi one..
45620         // not sure why we need all of this..
45621         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
45622         
45623         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
45624         //ss['background-attachment'] = 'fixed'; // w3c
45625         dbody.bgProperties = 'fixed'; // ie
45626         //Roo.DomHelper.applyStyles(dbody, ss);
45627         Roo.EventManager.on(this.doc, {
45628             //'mousedown': this.onEditorEvent,
45629             'mouseup': this.onEditorEvent,
45630             'dblclick': this.onEditorEvent,
45631             'click': this.onEditorEvent,
45632             'keyup': this.onEditorEvent,
45633             buffer:100,
45634             scope: this
45635         });
45636         if(Roo.isGecko){
45637             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
45638         }
45639         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
45640             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
45641         }
45642         this.initialized = true;
45643
45644         
45645         // initialize special key events - enter
45646         new Roo.htmleditor.KeyEnter({core : this});
45647         
45648         
45649         
45650         
45651         this.owner.fireEvent('initialize', this);
45652         this.pushValue();
45653     },
45654
45655     // private
45656     onDestroy : function(){
45657         
45658         
45659         
45660         if(this.rendered){
45661             
45662             //for (var i =0; i < this.toolbars.length;i++) {
45663             //    // fixme - ask toolbars for heights?
45664             //    this.toolbars[i].onDestroy();
45665            // }
45666             
45667             //this.wrap.dom.innerHTML = '';
45668             //this.wrap.remove();
45669         }
45670     },
45671
45672     // private
45673     onFirstFocus : function(){
45674         
45675         this.assignDocWin();
45676         
45677         
45678         this.activated = true;
45679          
45680     
45681         if(Roo.isGecko){ // prevent silly gecko errors
45682             this.win.focus();
45683             var s = this.win.getSelection();
45684             if(!s.focusNode || s.focusNode.nodeType != 3){
45685                 var r = s.getRangeAt(0);
45686                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
45687                 r.collapse(true);
45688                 this.deferFocus();
45689             }
45690             try{
45691                 this.execCmd('useCSS', true);
45692                 this.execCmd('styleWithCSS', false);
45693             }catch(e){}
45694         }
45695         this.owner.fireEvent('activate', this);
45696     },
45697
45698     // private
45699     adjustFont: function(btn){
45700         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
45701         //if(Roo.isSafari){ // safari
45702         //    adjust *= 2;
45703        // }
45704         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
45705         if(Roo.isSafari){ // safari
45706             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
45707             v =  (v < 10) ? 10 : v;
45708             v =  (v > 48) ? 48 : v;
45709             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
45710             
45711         }
45712         
45713         
45714         v = Math.max(1, v+adjust);
45715         
45716         this.execCmd('FontSize', v  );
45717     },
45718
45719     onEditorEvent : function(e)
45720     {
45721         this.owner.fireEvent('editorevent', this, e);
45722       //  this.updateToolbar();
45723         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
45724     },
45725
45726     insertTag : function(tg)
45727     {
45728         // could be a bit smarter... -> wrap the current selected tRoo..
45729         if (tg.toLowerCase() == 'span' ||
45730             tg.toLowerCase() == 'code' ||
45731             tg.toLowerCase() == 'sup' ||
45732             tg.toLowerCase() == 'sub' 
45733             ) {
45734             
45735             range = this.createRange(this.getSelection());
45736             var wrappingNode = this.doc.createElement(tg.toLowerCase());
45737             wrappingNode.appendChild(range.extractContents());
45738             range.insertNode(wrappingNode);
45739
45740             return;
45741             
45742             
45743             
45744         }
45745         this.execCmd("formatblock",   tg);
45746         
45747     },
45748     
45749     insertText : function(txt)
45750     {
45751         
45752         
45753         var range = this.createRange();
45754         range.deleteContents();
45755                //alert(Sender.getAttribute('label'));
45756                
45757         range.insertNode(this.doc.createTextNode(txt));
45758     } ,
45759     
45760      
45761
45762     /**
45763      * Executes a Midas editor command on the editor document and performs necessary focus and
45764      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
45765      * @param {String} cmd The Midas command
45766      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45767      */
45768     relayCmd : function(cmd, value){
45769         this.win.focus();
45770         this.execCmd(cmd, value);
45771         this.owner.fireEvent('editorevent', this);
45772         //this.updateToolbar();
45773         this.owner.deferFocus();
45774     },
45775
45776     /**
45777      * Executes a Midas editor command directly on the editor document.
45778      * For visual commands, you should use {@link #relayCmd} instead.
45779      * <b>This should only be called after the editor is initialized.</b>
45780      * @param {String} cmd The Midas command
45781      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45782      */
45783     execCmd : function(cmd, value){
45784         this.doc.execCommand(cmd, false, value === undefined ? null : value);
45785         this.syncValue();
45786     },
45787  
45788  
45789    
45790     /**
45791      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
45792      * to insert tRoo.
45793      * @param {String} text | dom node.. 
45794      */
45795     insertAtCursor : function(text)
45796     {
45797         
45798         if(!this.activated){
45799             return;
45800         }
45801         /*
45802         if(Roo.isIE){
45803             this.win.focus();
45804             var r = this.doc.selection.createRange();
45805             if(r){
45806                 r.collapse(true);
45807                 r.pasteHTML(text);
45808                 this.syncValue();
45809                 this.deferFocus();
45810             
45811             }
45812             return;
45813         }
45814         */
45815         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
45816             this.win.focus();
45817             
45818             
45819             // from jquery ui (MIT licenced)
45820             var range, node;
45821             var win = this.win;
45822             
45823             if (win.getSelection && win.getSelection().getRangeAt) {
45824                 range = win.getSelection().getRangeAt(0);
45825                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
45826                 range.insertNode(node);
45827             } else if (win.document.selection && win.document.selection.createRange) {
45828                 // no firefox support
45829                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45830                 win.document.selection.createRange().pasteHTML(txt);
45831             } else {
45832                 // no firefox support
45833                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45834                 this.execCmd('InsertHTML', txt);
45835             } 
45836             
45837             this.syncValue();
45838             
45839             this.deferFocus();
45840         }
45841     },
45842  // private
45843     mozKeyPress : function(e){
45844         if(e.ctrlKey){
45845             var c = e.getCharCode(), cmd;
45846           
45847             if(c > 0){
45848                 c = String.fromCharCode(c).toLowerCase();
45849                 switch(c){
45850                     case 'b':
45851                         cmd = 'bold';
45852                         break;
45853                     case 'i':
45854                         cmd = 'italic';
45855                         break;
45856                     
45857                     case 'u':
45858                         cmd = 'underline';
45859                         break;
45860                     
45861                     case 'v':
45862                         this.cleanUpPaste.defer(100, this);
45863                         return;
45864                         
45865                 }
45866                 if(cmd){
45867                     this.win.focus();
45868                     this.execCmd(cmd);
45869                     this.deferFocus();
45870                     e.preventDefault();
45871                 }
45872                 
45873             }
45874         }
45875     },
45876
45877     // private
45878     fixKeys : function(){ // load time branching for fastest keydown performance
45879         if(Roo.isIE){
45880             return function(e){
45881                 var k = e.getKey(), r;
45882                 if(k == e.TAB){
45883                     e.stopEvent();
45884                     r = this.doc.selection.createRange();
45885                     if(r){
45886                         r.collapse(true);
45887                         r.pasteHTML('&#160;&#160;&#160;&#160;');
45888                         this.deferFocus();
45889                     }
45890                     return;
45891                 }
45892                 
45893                 if(k == e.ENTER){
45894                     r = this.doc.selection.createRange();
45895                     if(r){
45896                         var target = r.parentElement();
45897                         if(!target || target.tagName.toLowerCase() != 'li'){
45898                             e.stopEvent();
45899                             r.pasteHTML('<br />');
45900                             r.collapse(false);
45901                             r.select();
45902                         }
45903                     }
45904                 }
45905                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45906                     this.cleanUpPaste.defer(100, this);
45907                     return;
45908                 }
45909                 
45910                 
45911             };
45912         }else if(Roo.isOpera){
45913             return function(e){
45914                 var k = e.getKey();
45915                 if(k == e.TAB){
45916                     e.stopEvent();
45917                     this.win.focus();
45918                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
45919                     this.deferFocus();
45920                 }
45921                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45922                     this.cleanUpPaste.defer(100, this);
45923                     return;
45924                 }
45925                 
45926             };
45927         }else if(Roo.isSafari){
45928             return function(e){
45929                 var k = e.getKey();
45930                 
45931                 if(k == e.TAB){
45932                     e.stopEvent();
45933                     this.execCmd('InsertText','\t');
45934                     this.deferFocus();
45935                     return;
45936                 }
45937                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45938                     this.cleanUpPaste.defer(100, this);
45939                     return;
45940                 }
45941                 
45942              };
45943         }
45944     }(),
45945     
45946     getAllAncestors: function()
45947     {
45948         var p = this.getSelectedNode();
45949         var a = [];
45950         if (!p) {
45951             a.push(p); // push blank onto stack..
45952             p = this.getParentElement();
45953         }
45954         
45955         
45956         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
45957             a.push(p);
45958             p = p.parentNode;
45959         }
45960         a.push(this.doc.body);
45961         return a;
45962     },
45963     lastSel : false,
45964     lastSelNode : false,
45965     
45966     
45967     getSelection : function() 
45968     {
45969         this.assignDocWin();
45970         return Roo.isIE ? this.doc.selection : this.win.getSelection();
45971     },
45972     
45973     getSelectedNode: function() 
45974     {
45975         // this may only work on Gecko!!!
45976         
45977         // should we cache this!!!!
45978         
45979         
45980         
45981          
45982         var range = this.createRange(this.getSelection()).cloneRange();
45983         
45984         if (Roo.isIE) {
45985             var parent = range.parentElement();
45986             while (true) {
45987                 var testRange = range.duplicate();
45988                 testRange.moveToElementText(parent);
45989                 if (testRange.inRange(range)) {
45990                     break;
45991                 }
45992                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
45993                     break;
45994                 }
45995                 parent = parent.parentElement;
45996             }
45997             return parent;
45998         }
45999         
46000         // is ancestor a text element.
46001         var ac =  range.commonAncestorContainer;
46002         if (ac.nodeType == 3) {
46003             ac = ac.parentNode;
46004         }
46005         
46006         var ar = ac.childNodes;
46007          
46008         var nodes = [];
46009         var other_nodes = [];
46010         var has_other_nodes = false;
46011         for (var i=0;i<ar.length;i++) {
46012             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
46013                 continue;
46014             }
46015             // fullly contained node.
46016             
46017             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
46018                 nodes.push(ar[i]);
46019                 continue;
46020             }
46021             
46022             // probably selected..
46023             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
46024                 other_nodes.push(ar[i]);
46025                 continue;
46026             }
46027             // outer..
46028             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
46029                 continue;
46030             }
46031             
46032             
46033             has_other_nodes = true;
46034         }
46035         if (!nodes.length && other_nodes.length) {
46036             nodes= other_nodes;
46037         }
46038         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
46039             return false;
46040         }
46041         
46042         return nodes[0];
46043     },
46044     createRange: function(sel)
46045     {
46046         // this has strange effects when using with 
46047         // top toolbar - not sure if it's a great idea.
46048         //this.editor.contentWindow.focus();
46049         if (typeof sel != "undefined") {
46050             try {
46051                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
46052             } catch(e) {
46053                 return this.doc.createRange();
46054             }
46055         } else {
46056             return this.doc.createRange();
46057         }
46058     },
46059     getParentElement: function()
46060     {
46061         
46062         this.assignDocWin();
46063         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
46064         
46065         var range = this.createRange(sel);
46066          
46067         try {
46068             var p = range.commonAncestorContainer;
46069             while (p.nodeType == 3) { // text node
46070                 p = p.parentNode;
46071             }
46072             return p;
46073         } catch (e) {
46074             return null;
46075         }
46076     
46077     },
46078     /***
46079      *
46080      * Range intersection.. the hard stuff...
46081      *  '-1' = before
46082      *  '0' = hits..
46083      *  '1' = after.
46084      *         [ -- selected range --- ]
46085      *   [fail]                        [fail]
46086      *
46087      *    basically..
46088      *      if end is before start or  hits it. fail.
46089      *      if start is after end or hits it fail.
46090      *
46091      *   if either hits (but other is outside. - then it's not 
46092      *   
46093      *    
46094      **/
46095     
46096     
46097     // @see http://www.thismuchiknow.co.uk/?p=64.
46098     rangeIntersectsNode : function(range, node)
46099     {
46100         var nodeRange = node.ownerDocument.createRange();
46101         try {
46102             nodeRange.selectNode(node);
46103         } catch (e) {
46104             nodeRange.selectNodeContents(node);
46105         }
46106     
46107         var rangeStartRange = range.cloneRange();
46108         rangeStartRange.collapse(true);
46109     
46110         var rangeEndRange = range.cloneRange();
46111         rangeEndRange.collapse(false);
46112     
46113         var nodeStartRange = nodeRange.cloneRange();
46114         nodeStartRange.collapse(true);
46115     
46116         var nodeEndRange = nodeRange.cloneRange();
46117         nodeEndRange.collapse(false);
46118     
46119         return rangeStartRange.compareBoundaryPoints(
46120                  Range.START_TO_START, nodeEndRange) == -1 &&
46121                rangeEndRange.compareBoundaryPoints(
46122                  Range.START_TO_START, nodeStartRange) == 1;
46123         
46124          
46125     },
46126     rangeCompareNode : function(range, node)
46127     {
46128         var nodeRange = node.ownerDocument.createRange();
46129         try {
46130             nodeRange.selectNode(node);
46131         } catch (e) {
46132             nodeRange.selectNodeContents(node);
46133         }
46134         
46135         
46136         range.collapse(true);
46137     
46138         nodeRange.collapse(true);
46139      
46140         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
46141         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
46142          
46143         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
46144         
46145         var nodeIsBefore   =  ss == 1;
46146         var nodeIsAfter    = ee == -1;
46147         
46148         if (nodeIsBefore && nodeIsAfter) {
46149             return 0; // outer
46150         }
46151         if (!nodeIsBefore && nodeIsAfter) {
46152             return 1; //right trailed.
46153         }
46154         
46155         if (nodeIsBefore && !nodeIsAfter) {
46156             return 2;  // left trailed.
46157         }
46158         // fully contined.
46159         return 3;
46160     },
46161
46162     // private? - in a new class?
46163     cleanUpPaste :  function()
46164     {
46165         // cleans up the whole document..
46166         Roo.log('cleanuppaste');
46167         
46168         this.cleanUpChild(this.doc.body);
46169         var clean = this.cleanWordChars(this.doc.body.innerHTML);
46170         if (clean != this.doc.body.innerHTML) {
46171             this.doc.body.innerHTML = clean;
46172         }
46173         
46174     },
46175     
46176     cleanWordChars : function(input) {// change the chars to hex code
46177         var he = Roo.HtmlEditorCore;
46178         
46179         var output = input;
46180         Roo.each(he.swapCodes, function(sw) { 
46181             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
46182             
46183             output = output.replace(swapper, sw[1]);
46184         });
46185         
46186         return output;
46187     },
46188     
46189      
46190     
46191         
46192     
46193     cleanUpChild : function (node)
46194     {
46195         
46196         new Roo.htmleditor.FilterComment({node : node});
46197         new Roo.htmleditor.FilterAttributes({
46198                 node : node,
46199                 attrib_black : this.ablack,
46200                 attrib_clean : this.aclean,
46201                 style_white : this.cwhite,
46202                 style_black : this.cblack
46203         });
46204         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
46205         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
46206          
46207         
46208     },
46209     
46210     /**
46211      * Clean up MS wordisms...
46212      * @deprecated - use filter directly
46213      */
46214     cleanWord : function(node)
46215     {
46216         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
46217         
46218     },
46219    
46220     
46221     /**
46222
46223      * @deprecated - use filters
46224      */
46225     cleanTableWidths : function(node)
46226     {
46227         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
46228         
46229  
46230     },
46231     
46232      
46233         
46234     applyBlacklists : function()
46235     {
46236         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
46237         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
46238         
46239         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
46240         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
46241         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
46242         
46243         this.white = [];
46244         this.black = [];
46245         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
46246             if (b.indexOf(tag) > -1) {
46247                 return;
46248             }
46249             this.white.push(tag);
46250             
46251         }, this);
46252         
46253         Roo.each(w, function(tag) {
46254             if (b.indexOf(tag) > -1) {
46255                 return;
46256             }
46257             if (this.white.indexOf(tag) > -1) {
46258                 return;
46259             }
46260             this.white.push(tag);
46261             
46262         }, this);
46263         
46264         
46265         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
46266             if (w.indexOf(tag) > -1) {
46267                 return;
46268             }
46269             this.black.push(tag);
46270             
46271         }, this);
46272         
46273         Roo.each(b, function(tag) {
46274             if (w.indexOf(tag) > -1) {
46275                 return;
46276             }
46277             if (this.black.indexOf(tag) > -1) {
46278                 return;
46279             }
46280             this.black.push(tag);
46281             
46282         }, this);
46283         
46284         
46285         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
46286         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
46287         
46288         this.cwhite = [];
46289         this.cblack = [];
46290         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
46291             if (b.indexOf(tag) > -1) {
46292                 return;
46293             }
46294             this.cwhite.push(tag);
46295             
46296         }, this);
46297         
46298         Roo.each(w, function(tag) {
46299             if (b.indexOf(tag) > -1) {
46300                 return;
46301             }
46302             if (this.cwhite.indexOf(tag) > -1) {
46303                 return;
46304             }
46305             this.cwhite.push(tag);
46306             
46307         }, this);
46308         
46309         
46310         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
46311             if (w.indexOf(tag) > -1) {
46312                 return;
46313             }
46314             this.cblack.push(tag);
46315             
46316         }, this);
46317         
46318         Roo.each(b, function(tag) {
46319             if (w.indexOf(tag) > -1) {
46320                 return;
46321             }
46322             if (this.cblack.indexOf(tag) > -1) {
46323                 return;
46324             }
46325             this.cblack.push(tag);
46326             
46327         }, this);
46328     },
46329     
46330     setStylesheets : function(stylesheets)
46331     {
46332         if(typeof(stylesheets) == 'string'){
46333             Roo.get(this.iframe.contentDocument.head).createChild({
46334                 tag : 'link',
46335                 rel : 'stylesheet',
46336                 type : 'text/css',
46337                 href : stylesheets
46338             });
46339             
46340             return;
46341         }
46342         var _this = this;
46343      
46344         Roo.each(stylesheets, function(s) {
46345             if(!s.length){
46346                 return;
46347             }
46348             
46349             Roo.get(_this.iframe.contentDocument.head).createChild({
46350                 tag : 'link',
46351                 rel : 'stylesheet',
46352                 type : 'text/css',
46353                 href : s
46354             });
46355         });
46356
46357         
46358     },
46359     
46360     removeStylesheets : function()
46361     {
46362         var _this = this;
46363         
46364         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46365             s.remove();
46366         });
46367     },
46368     
46369     setStyle : function(style)
46370     {
46371         Roo.get(this.iframe.contentDocument.head).createChild({
46372             tag : 'style',
46373             type : 'text/css',
46374             html : style
46375         });
46376
46377         return;
46378     }
46379     
46380     // hide stuff that is not compatible
46381     /**
46382      * @event blur
46383      * @hide
46384      */
46385     /**
46386      * @event change
46387      * @hide
46388      */
46389     /**
46390      * @event focus
46391      * @hide
46392      */
46393     /**
46394      * @event specialkey
46395      * @hide
46396      */
46397     /**
46398      * @cfg {String} fieldClass @hide
46399      */
46400     /**
46401      * @cfg {String} focusClass @hide
46402      */
46403     /**
46404      * @cfg {String} autoCreate @hide
46405      */
46406     /**
46407      * @cfg {String} inputType @hide
46408      */
46409     /**
46410      * @cfg {String} invalidClass @hide
46411      */
46412     /**
46413      * @cfg {String} invalidText @hide
46414      */
46415     /**
46416      * @cfg {String} msgFx @hide
46417      */
46418     /**
46419      * @cfg {String} validateOnBlur @hide
46420      */
46421 });
46422
46423 Roo.HtmlEditorCore.white = [
46424         'area', 'br', 'img', 'input', 'hr', 'wbr',
46425         
46426        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
46427        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
46428        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
46429        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
46430        'table',   'ul',         'xmp', 
46431        
46432        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
46433       'thead',   'tr', 
46434      
46435       'dir', 'menu', 'ol', 'ul', 'dl',
46436        
46437       'embed',  'object'
46438 ];
46439
46440
46441 Roo.HtmlEditorCore.black = [
46442     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46443         'applet', // 
46444         'base',   'basefont', 'bgsound', 'blink',  'body', 
46445         'frame',  'frameset', 'head',    'html',   'ilayer', 
46446         'iframe', 'layer',  'link',     'meta',    'object',   
46447         'script', 'style' ,'title',  'xml' // clean later..
46448 ];
46449 Roo.HtmlEditorCore.clean = [
46450     'script', 'style', 'title', 'xml'
46451 ];
46452 Roo.HtmlEditorCore.tag_remove = [
46453     'font'
46454 ];
46455 // attributes..
46456
46457 Roo.HtmlEditorCore.ablack = [
46458     'on'
46459 ];
46460     
46461 Roo.HtmlEditorCore.aclean = [ 
46462     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46463 ];
46464
46465 // protocols..
46466 Roo.HtmlEditorCore.pwhite= [
46467         'http',  'https',  'mailto'
46468 ];
46469
46470 // white listed style attributes.
46471 Roo.HtmlEditorCore.cwhite= [
46472       //  'text-align', /// default is to allow most things..
46473       
46474          
46475 //        'font-size'//??
46476 ];
46477
46478 // black listed style attributes.
46479 Roo.HtmlEditorCore.cblack= [
46480       //  'font-size' -- this can be set by the project 
46481 ];
46482
46483
46484 Roo.HtmlEditorCore.swapCodes   =[ 
46485     [    8211, "&#8211;" ], 
46486     [    8212, "&#8212;" ], 
46487     [    8216,  "'" ],  
46488     [    8217, "'" ],  
46489     [    8220, '"' ],  
46490     [    8221, '"' ],  
46491     [    8226, "*" ],  
46492     [    8230, "..." ]
46493 ]; 
46494
46495     //<script type="text/javascript">
46496
46497 /*
46498  * Ext JS Library 1.1.1
46499  * Copyright(c) 2006-2007, Ext JS, LLC.
46500  * Licence LGPL
46501  * 
46502  */
46503  
46504  
46505 Roo.form.HtmlEditor = function(config){
46506     
46507     
46508     
46509     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
46510     
46511     if (!this.toolbars) {
46512         this.toolbars = [];
46513     }
46514     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
46515     
46516     
46517 };
46518
46519 /**
46520  * @class Roo.form.HtmlEditor
46521  * @extends Roo.form.Field
46522  * Provides a lightweight HTML Editor component.
46523  *
46524  * This has been tested on Fireforx / Chrome.. IE may not be so great..
46525  * 
46526  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
46527  * supported by this editor.</b><br/><br/>
46528  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
46529  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46530  */
46531 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
46532     /**
46533      * @cfg {Boolean} clearUp
46534      */
46535     clearUp : true,
46536       /**
46537      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
46538      */
46539     toolbars : false,
46540    
46541      /**
46542      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46543      *                        Roo.resizable.
46544      */
46545     resizable : false,
46546      /**
46547      * @cfg {Number} height (in pixels)
46548      */   
46549     height: 300,
46550    /**
46551      * @cfg {Number} width (in pixels)
46552      */   
46553     width: 500,
46554     
46555     /**
46556      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
46557      * 
46558      */
46559     stylesheets: false,
46560     
46561     
46562      /**
46563      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
46564      * 
46565      */
46566     cblack: false,
46567     /**
46568      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
46569      * 
46570      */
46571     cwhite: false,
46572     
46573      /**
46574      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
46575      * 
46576      */
46577     black: false,
46578     /**
46579      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
46580      * 
46581      */
46582     white: false,
46583     /**
46584      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46585      */
46586     allowComments: false,
46587     /**
46588      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
46589      */
46590     
46591     
46592      bodyCls : '',
46593     
46594     // id of frame..
46595     frameId: false,
46596     
46597     // private properties
46598     validationEvent : false,
46599     deferHeight: true,
46600     initialized : false,
46601     activated : false,
46602     
46603     onFocus : Roo.emptyFn,
46604     iframePad:3,
46605     hideMode:'offsets',
46606     
46607     actionMode : 'container', // defaults to hiding it...
46608     
46609     defaultAutoCreate : { // modified by initCompnoent..
46610         tag: "textarea",
46611         style:"width:500px;height:300px;",
46612         autocomplete: "new-password"
46613     },
46614
46615     // private
46616     initComponent : function(){
46617         this.addEvents({
46618             /**
46619              * @event initialize
46620              * Fires when the editor is fully initialized (including the iframe)
46621              * @param {HtmlEditor} this
46622              */
46623             initialize: true,
46624             /**
46625              * @event activate
46626              * Fires when the editor is first receives the focus. Any insertion must wait
46627              * until after this event.
46628              * @param {HtmlEditor} this
46629              */
46630             activate: true,
46631              /**
46632              * @event beforesync
46633              * Fires before the textarea is updated with content from the editor iframe. Return false
46634              * to cancel the sync.
46635              * @param {HtmlEditor} this
46636              * @param {String} html
46637              */
46638             beforesync: true,
46639              /**
46640              * @event beforepush
46641              * Fires before the iframe editor is updated with content from the textarea. Return false
46642              * to cancel the push.
46643              * @param {HtmlEditor} this
46644              * @param {String} html
46645              */
46646             beforepush: true,
46647              /**
46648              * @event sync
46649              * Fires when the textarea is updated with content from the editor iframe.
46650              * @param {HtmlEditor} this
46651              * @param {String} html
46652              */
46653             sync: true,
46654              /**
46655              * @event push
46656              * Fires when the iframe editor is updated with content from the textarea.
46657              * @param {HtmlEditor} this
46658              * @param {String} html
46659              */
46660             push: true,
46661              /**
46662              * @event editmodechange
46663              * Fires when the editor switches edit modes
46664              * @param {HtmlEditor} this
46665              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
46666              */
46667             editmodechange: true,
46668             /**
46669              * @event editorevent
46670              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46671              * @param {HtmlEditor} this
46672              */
46673             editorevent: true,
46674             /**
46675              * @event firstfocus
46676              * Fires when on first focus - needed by toolbars..
46677              * @param {HtmlEditor} this
46678              */
46679             firstfocus: true,
46680             /**
46681              * @event autosave
46682              * Auto save the htmlEditor value as a file into Events
46683              * @param {HtmlEditor} this
46684              */
46685             autosave: true,
46686             /**
46687              * @event savedpreview
46688              * preview the saved version of htmlEditor
46689              * @param {HtmlEditor} this
46690              */
46691             savedpreview: true,
46692             
46693             /**
46694             * @event stylesheetsclick
46695             * Fires when press the Sytlesheets button
46696             * @param {Roo.HtmlEditorCore} this
46697             */
46698             stylesheetsclick: true
46699         });
46700         this.defaultAutoCreate =  {
46701             tag: "textarea",
46702             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
46703             autocomplete: "new-password"
46704         };
46705     },
46706
46707     /**
46708      * Protected method that will not generally be called directly. It
46709      * is called when the editor creates its toolbar. Override this method if you need to
46710      * add custom toolbar buttons.
46711      * @param {HtmlEditor} editor
46712      */
46713     createToolbar : function(editor){
46714         Roo.log("create toolbars");
46715         if (!editor.toolbars || !editor.toolbars.length) {
46716             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
46717         }
46718         
46719         for (var i =0 ; i < editor.toolbars.length;i++) {
46720             editor.toolbars[i] = Roo.factory(
46721                     typeof(editor.toolbars[i]) == 'string' ?
46722                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
46723                 Roo.form.HtmlEditor);
46724             editor.toolbars[i].init(editor);
46725         }
46726          
46727         
46728     },
46729
46730      
46731     // private
46732     onRender : function(ct, position)
46733     {
46734         var _t = this;
46735         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
46736         
46737         this.wrap = this.el.wrap({
46738             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
46739         });
46740         
46741         this.editorcore.onRender(ct, position);
46742          
46743         if (this.resizable) {
46744             this.resizeEl = new Roo.Resizable(this.wrap, {
46745                 pinned : true,
46746                 wrap: true,
46747                 dynamic : true,
46748                 minHeight : this.height,
46749                 height: this.height,
46750                 handles : this.resizable,
46751                 width: this.width,
46752                 listeners : {
46753                     resize : function(r, w, h) {
46754                         _t.onResize(w,h); // -something
46755                     }
46756                 }
46757             });
46758             
46759         }
46760         this.createToolbar(this);
46761        
46762         
46763         if(!this.width){
46764             this.setSize(this.wrap.getSize());
46765         }
46766         if (this.resizeEl) {
46767             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
46768             // should trigger onReize..
46769         }
46770         
46771         this.keyNav = new Roo.KeyNav(this.el, {
46772             
46773             "tab" : function(e){
46774                 e.preventDefault();
46775                 
46776                 var value = this.getValue();
46777                 
46778                 var start = this.el.dom.selectionStart;
46779                 var end = this.el.dom.selectionEnd;
46780                 
46781                 if(!e.shiftKey){
46782                     
46783                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
46784                     this.el.dom.setSelectionRange(end + 1, end + 1);
46785                     return;
46786                 }
46787                 
46788                 var f = value.substring(0, start).split("\t");
46789                 
46790                 if(f.pop().length != 0){
46791                     return;
46792                 }
46793                 
46794                 this.setValue(f.join("\t") + value.substring(end));
46795                 this.el.dom.setSelectionRange(start - 1, start - 1);
46796                 
46797             },
46798             
46799             "home" : function(e){
46800                 e.preventDefault();
46801                 
46802                 var curr = this.el.dom.selectionStart;
46803                 var lines = this.getValue().split("\n");
46804                 
46805                 if(!lines.length){
46806                     return;
46807                 }
46808                 
46809                 if(e.ctrlKey){
46810                     this.el.dom.setSelectionRange(0, 0);
46811                     return;
46812                 }
46813                 
46814                 var pos = 0;
46815                 
46816                 for (var i = 0; i < lines.length;i++) {
46817                     pos += lines[i].length;
46818                     
46819                     if(i != 0){
46820                         pos += 1;
46821                     }
46822                     
46823                     if(pos < curr){
46824                         continue;
46825                     }
46826                     
46827                     pos -= lines[i].length;
46828                     
46829                     break;
46830                 }
46831                 
46832                 if(!e.shiftKey){
46833                     this.el.dom.setSelectionRange(pos, pos);
46834                     return;
46835                 }
46836                 
46837                 this.el.dom.selectionStart = pos;
46838                 this.el.dom.selectionEnd = curr;
46839             },
46840             
46841             "end" : function(e){
46842                 e.preventDefault();
46843                 
46844                 var curr = this.el.dom.selectionStart;
46845                 var lines = this.getValue().split("\n");
46846                 
46847                 if(!lines.length){
46848                     return;
46849                 }
46850                 
46851                 if(e.ctrlKey){
46852                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
46853                     return;
46854                 }
46855                 
46856                 var pos = 0;
46857                 
46858                 for (var i = 0; i < lines.length;i++) {
46859                     
46860                     pos += lines[i].length;
46861                     
46862                     if(i != 0){
46863                         pos += 1;
46864                     }
46865                     
46866                     if(pos < curr){
46867                         continue;
46868                     }
46869                     
46870                     break;
46871                 }
46872                 
46873                 if(!e.shiftKey){
46874                     this.el.dom.setSelectionRange(pos, pos);
46875                     return;
46876                 }
46877                 
46878                 this.el.dom.selectionStart = curr;
46879                 this.el.dom.selectionEnd = pos;
46880             },
46881
46882             scope : this,
46883
46884             doRelay : function(foo, bar, hname){
46885                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46886             },
46887
46888             forceKeyDown: true
46889         });
46890         
46891 //        if(this.autosave && this.w){
46892 //            this.autoSaveFn = setInterval(this.autosave, 1000);
46893 //        }
46894     },
46895
46896     // private
46897     onResize : function(w, h)
46898     {
46899         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
46900         var ew = false;
46901         var eh = false;
46902         
46903         if(this.el ){
46904             if(typeof w == 'number'){
46905                 var aw = w - this.wrap.getFrameWidth('lr');
46906                 this.el.setWidth(this.adjustWidth('textarea', aw));
46907                 ew = aw;
46908             }
46909             if(typeof h == 'number'){
46910                 var tbh = 0;
46911                 for (var i =0; i < this.toolbars.length;i++) {
46912                     // fixme - ask toolbars for heights?
46913                     tbh += this.toolbars[i].tb.el.getHeight();
46914                     if (this.toolbars[i].footer) {
46915                         tbh += this.toolbars[i].footer.el.getHeight();
46916                     }
46917                 }
46918                 
46919                 
46920                 
46921                 
46922                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
46923                 ah -= 5; // knock a few pixes off for look..
46924 //                Roo.log(ah);
46925                 this.el.setHeight(this.adjustWidth('textarea', ah));
46926                 var eh = ah;
46927             }
46928         }
46929         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
46930         this.editorcore.onResize(ew,eh);
46931         
46932     },
46933
46934     /**
46935      * Toggles the editor between standard and source edit mode.
46936      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46937      */
46938     toggleSourceEdit : function(sourceEditMode)
46939     {
46940         this.editorcore.toggleSourceEdit(sourceEditMode);
46941         
46942         if(this.editorcore.sourceEditMode){
46943             Roo.log('editor - showing textarea');
46944             
46945 //            Roo.log('in');
46946 //            Roo.log(this.syncValue());
46947             this.editorcore.syncValue();
46948             this.el.removeClass('x-hidden');
46949             this.el.dom.removeAttribute('tabIndex');
46950             this.el.focus();
46951             this.el.dom.scrollTop = 0;
46952             
46953             
46954             for (var i = 0; i < this.toolbars.length; i++) {
46955                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46956                     this.toolbars[i].tb.hide();
46957                     this.toolbars[i].footer.hide();
46958                 }
46959             }
46960             
46961         }else{
46962             Roo.log('editor - hiding textarea');
46963 //            Roo.log('out')
46964 //            Roo.log(this.pushValue()); 
46965             this.editorcore.pushValue();
46966             
46967             this.el.addClass('x-hidden');
46968             this.el.dom.setAttribute('tabIndex', -1);
46969             
46970             for (var i = 0; i < this.toolbars.length; i++) {
46971                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46972                     this.toolbars[i].tb.show();
46973                     this.toolbars[i].footer.show();
46974                 }
46975             }
46976             
46977             //this.deferFocus();
46978         }
46979         
46980         this.setSize(this.wrap.getSize());
46981         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
46982         
46983         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
46984     },
46985  
46986     // private (for BoxComponent)
46987     adjustSize : Roo.BoxComponent.prototype.adjustSize,
46988
46989     // private (for BoxComponent)
46990     getResizeEl : function(){
46991         return this.wrap;
46992     },
46993
46994     // private (for BoxComponent)
46995     getPositionEl : function(){
46996         return this.wrap;
46997     },
46998
46999     // private
47000     initEvents : function(){
47001         this.originalValue = this.getValue();
47002     },
47003
47004     /**
47005      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47006      * @method
47007      */
47008     markInvalid : Roo.emptyFn,
47009     /**
47010      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47011      * @method
47012      */
47013     clearInvalid : Roo.emptyFn,
47014
47015     setValue : function(v){
47016         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
47017         this.editorcore.pushValue();
47018     },
47019
47020      
47021     // private
47022     deferFocus : function(){
47023         this.focus.defer(10, this);
47024     },
47025
47026     // doc'ed in Field
47027     focus : function(){
47028         this.editorcore.focus();
47029         
47030     },
47031       
47032
47033     // private
47034     onDestroy : function(){
47035         
47036         
47037         
47038         if(this.rendered){
47039             
47040             for (var i =0; i < this.toolbars.length;i++) {
47041                 // fixme - ask toolbars for heights?
47042                 this.toolbars[i].onDestroy();
47043             }
47044             
47045             this.wrap.dom.innerHTML = '';
47046             this.wrap.remove();
47047         }
47048     },
47049
47050     // private
47051     onFirstFocus : function(){
47052         //Roo.log("onFirstFocus");
47053         this.editorcore.onFirstFocus();
47054          for (var i =0; i < this.toolbars.length;i++) {
47055             this.toolbars[i].onFirstFocus();
47056         }
47057         
47058     },
47059     
47060     // private
47061     syncValue : function()
47062     {
47063         this.editorcore.syncValue();
47064     },
47065     
47066     pushValue : function()
47067     {
47068         this.editorcore.pushValue();
47069     },
47070     
47071     setStylesheets : function(stylesheets)
47072     {
47073         this.editorcore.setStylesheets(stylesheets);
47074     },
47075     
47076     removeStylesheets : function()
47077     {
47078         this.editorcore.removeStylesheets();
47079     }
47080      
47081     
47082     // hide stuff that is not compatible
47083     /**
47084      * @event blur
47085      * @hide
47086      */
47087     /**
47088      * @event change
47089      * @hide
47090      */
47091     /**
47092      * @event focus
47093      * @hide
47094      */
47095     /**
47096      * @event specialkey
47097      * @hide
47098      */
47099     /**
47100      * @cfg {String} fieldClass @hide
47101      */
47102     /**
47103      * @cfg {String} focusClass @hide
47104      */
47105     /**
47106      * @cfg {String} autoCreate @hide
47107      */
47108     /**
47109      * @cfg {String} inputType @hide
47110      */
47111     /**
47112      * @cfg {String} invalidClass @hide
47113      */
47114     /**
47115      * @cfg {String} invalidText @hide
47116      */
47117     /**
47118      * @cfg {String} msgFx @hide
47119      */
47120     /**
47121      * @cfg {String} validateOnBlur @hide
47122      */
47123 });
47124  
47125     // <script type="text/javascript">
47126 /*
47127  * Based on
47128  * Ext JS Library 1.1.1
47129  * Copyright(c) 2006-2007, Ext JS, LLC.
47130  *  
47131  
47132  */
47133
47134 /**
47135  * @class Roo.form.HtmlEditorToolbar1
47136  * Basic Toolbar
47137  * 
47138  * Usage:
47139  *
47140  new Roo.form.HtmlEditor({
47141     ....
47142     toolbars : [
47143         new Roo.form.HtmlEditorToolbar1({
47144             disable : { fonts: 1 , format: 1, ..., ... , ...],
47145             btns : [ .... ]
47146         })
47147     }
47148      
47149  * 
47150  * @cfg {Object} disable List of elements to disable..
47151  * @cfg {Array} btns List of additional buttons.
47152  * 
47153  * 
47154  * NEEDS Extra CSS? 
47155  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
47156  */
47157  
47158 Roo.form.HtmlEditor.ToolbarStandard = function(config)
47159 {
47160     
47161     Roo.apply(this, config);
47162     
47163     // default disabled, based on 'good practice'..
47164     this.disable = this.disable || {};
47165     Roo.applyIf(this.disable, {
47166         fontSize : true,
47167         colors : true,
47168         specialElements : true
47169     });
47170     
47171     
47172     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47173     // dont call parent... till later.
47174 }
47175
47176 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
47177     
47178     tb: false,
47179     
47180     rendered: false,
47181     
47182     editor : false,
47183     editorcore : false,
47184     /**
47185      * @cfg {Object} disable  List of toolbar elements to disable
47186          
47187      */
47188     disable : false,
47189     
47190     
47191      /**
47192      * @cfg {String} createLinkText The default text for the create link prompt
47193      */
47194     createLinkText : 'Please enter the URL for the link:',
47195     /**
47196      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
47197      */
47198     defaultLinkValue : 'http:/'+'/',
47199    
47200     
47201       /**
47202      * @cfg {Array} fontFamilies An array of available font families
47203      */
47204     fontFamilies : [
47205         'Arial',
47206         'Courier New',
47207         'Tahoma',
47208         'Times New Roman',
47209         'Verdana'
47210     ],
47211     
47212     specialChars : [
47213            "&#169;",
47214           "&#174;",     
47215           "&#8482;",    
47216           "&#163;" ,    
47217          // "&#8212;",    
47218           "&#8230;",    
47219           "&#247;" ,    
47220         //  "&#225;" ,     ?? a acute?
47221            "&#8364;"    , //Euro
47222        //   "&#8220;"    ,
47223         //  "&#8221;"    ,
47224         //  "&#8226;"    ,
47225           "&#176;"  //   , // degrees
47226
47227          // "&#233;"     , // e ecute
47228          // "&#250;"     , // u ecute?
47229     ],
47230     
47231     specialElements : [
47232         {
47233             text: "Insert Table",
47234             xtype: 'MenuItem',
47235             xns : Roo.Menu,
47236             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
47237                 
47238         },
47239         {    
47240             text: "Insert Image",
47241             xtype: 'MenuItem',
47242             xns : Roo.Menu,
47243             ihtml : '<img src="about:blank"/>'
47244             
47245         }
47246         
47247          
47248     ],
47249     
47250     
47251     inputElements : [ 
47252             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
47253             "input:submit", "input:button", "select", "textarea", "label" ],
47254     formats : [
47255         ["p"] ,  
47256         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
47257         ["pre"],[ "code"], 
47258         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
47259         ['div'],['span'],
47260         ['sup'],['sub']
47261     ],
47262     
47263     cleanStyles : [
47264         "font-size"
47265     ],
47266      /**
47267      * @cfg {String} defaultFont default font to use.
47268      */
47269     defaultFont: 'tahoma',
47270    
47271     fontSelect : false,
47272     
47273     
47274     formatCombo : false,
47275     
47276     init : function(editor)
47277     {
47278         this.editor = editor;
47279         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47280         var editorcore = this.editorcore;
47281         
47282         var _t = this;
47283         
47284         var fid = editorcore.frameId;
47285         var etb = this;
47286         function btn(id, toggle, handler){
47287             var xid = fid + '-'+ id ;
47288             return {
47289                 id : xid,
47290                 cmd : id,
47291                 cls : 'x-btn-icon x-edit-'+id,
47292                 enableToggle:toggle !== false,
47293                 scope: _t, // was editor...
47294                 handler:handler||_t.relayBtnCmd,
47295                 clickEvent:'mousedown',
47296                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47297                 tabIndex:-1
47298             };
47299         }
47300         
47301         
47302         
47303         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47304         this.tb = tb;
47305          // stop form submits
47306         tb.el.on('click', function(e){
47307             e.preventDefault(); // what does this do?
47308         });
47309
47310         if(!this.disable.font) { // && !Roo.isSafari){
47311             /* why no safari for fonts 
47312             editor.fontSelect = tb.el.createChild({
47313                 tag:'select',
47314                 tabIndex: -1,
47315                 cls:'x-font-select',
47316                 html: this.createFontOptions()
47317             });
47318             
47319             editor.fontSelect.on('change', function(){
47320                 var font = editor.fontSelect.dom.value;
47321                 editor.relayCmd('fontname', font);
47322                 editor.deferFocus();
47323             }, editor);
47324             
47325             tb.add(
47326                 editor.fontSelect.dom,
47327                 '-'
47328             );
47329             */
47330             
47331         };
47332         if(!this.disable.formats){
47333             this.formatCombo = new Roo.form.ComboBox({
47334                 store: new Roo.data.SimpleStore({
47335                     id : 'tag',
47336                     fields: ['tag'],
47337                     data : this.formats // from states.js
47338                 }),
47339                 blockFocus : true,
47340                 name : '',
47341                 //autoCreate : {tag: "div",  size: "20"},
47342                 displayField:'tag',
47343                 typeAhead: false,
47344                 mode: 'local',
47345                 editable : false,
47346                 triggerAction: 'all',
47347                 emptyText:'Add tag',
47348                 selectOnFocus:true,
47349                 width:135,
47350                 listeners : {
47351                     'select': function(c, r, i) {
47352                         editorcore.insertTag(r.get('tag'));
47353                         editor.focus();
47354                     }
47355                 }
47356
47357             });
47358             tb.addField(this.formatCombo);
47359             
47360         }
47361         
47362         if(!this.disable.format){
47363             tb.add(
47364                 btn('bold'),
47365                 btn('italic'),
47366                 btn('underline'),
47367                 btn('strikethrough')
47368             );
47369         };
47370         if(!this.disable.fontSize){
47371             tb.add(
47372                 '-',
47373                 
47374                 
47375                 btn('increasefontsize', false, editorcore.adjustFont),
47376                 btn('decreasefontsize', false, editorcore.adjustFont)
47377             );
47378         };
47379         
47380         
47381         if(!this.disable.colors){
47382             tb.add(
47383                 '-', {
47384                     id:editorcore.frameId +'-forecolor',
47385                     cls:'x-btn-icon x-edit-forecolor',
47386                     clickEvent:'mousedown',
47387                     tooltip: this.buttonTips['forecolor'] || undefined,
47388                     tabIndex:-1,
47389                     menu : new Roo.menu.ColorMenu({
47390                         allowReselect: true,
47391                         focus: Roo.emptyFn,
47392                         value:'000000',
47393                         plain:true,
47394                         selectHandler: function(cp, color){
47395                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47396                             editor.deferFocus();
47397                         },
47398                         scope: editorcore,
47399                         clickEvent:'mousedown'
47400                     })
47401                 }, {
47402                     id:editorcore.frameId +'backcolor',
47403                     cls:'x-btn-icon x-edit-backcolor',
47404                     clickEvent:'mousedown',
47405                     tooltip: this.buttonTips['backcolor'] || undefined,
47406                     tabIndex:-1,
47407                     menu : new Roo.menu.ColorMenu({
47408                         focus: Roo.emptyFn,
47409                         value:'FFFFFF',
47410                         plain:true,
47411                         allowReselect: true,
47412                         selectHandler: function(cp, color){
47413                             if(Roo.isGecko){
47414                                 editorcore.execCmd('useCSS', false);
47415                                 editorcore.execCmd('hilitecolor', color);
47416                                 editorcore.execCmd('useCSS', true);
47417                                 editor.deferFocus();
47418                             }else{
47419                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47420                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47421                                 editor.deferFocus();
47422                             }
47423                         },
47424                         scope:editorcore,
47425                         clickEvent:'mousedown'
47426                     })
47427                 }
47428             );
47429         };
47430         // now add all the items...
47431         
47432
47433         if(!this.disable.alignments){
47434             tb.add(
47435                 '-',
47436                 btn('justifyleft'),
47437                 btn('justifycenter'),
47438                 btn('justifyright')
47439             );
47440         };
47441
47442         //if(!Roo.isSafari){
47443             if(!this.disable.links){
47444                 tb.add(
47445                     '-',
47446                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47447                 );
47448             };
47449
47450             if(!this.disable.lists){
47451                 tb.add(
47452                     '-',
47453                     btn('insertorderedlist'),
47454                     btn('insertunorderedlist')
47455                 );
47456             }
47457             if(!this.disable.sourceEdit){
47458                 tb.add(
47459                     '-',
47460                     btn('sourceedit', true, function(btn){
47461                         this.toggleSourceEdit(btn.pressed);
47462                     })
47463                 );
47464             }
47465         //}
47466         
47467         var smenu = { };
47468         // special menu.. - needs to be tidied up..
47469         if (!this.disable.special) {
47470             smenu = {
47471                 text: "&#169;",
47472                 cls: 'x-edit-none',
47473                 
47474                 menu : {
47475                     items : []
47476                 }
47477             };
47478             for (var i =0; i < this.specialChars.length; i++) {
47479                 smenu.menu.items.push({
47480                     
47481                     html: this.specialChars[i],
47482                     handler: function(a,b) {
47483                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47484                         //editor.insertAtCursor(a.html);
47485                         
47486                     },
47487                     tabIndex:-1
47488                 });
47489             }
47490             
47491             
47492             tb.add(smenu);
47493             
47494             
47495         }
47496         
47497         var cmenu = { };
47498         if (!this.disable.cleanStyles) {
47499             cmenu = {
47500                 cls: 'x-btn-icon x-btn-clear',
47501                 
47502                 menu : {
47503                     items : []
47504                 }
47505             };
47506             for (var i =0; i < this.cleanStyles.length; i++) {
47507                 cmenu.menu.items.push({
47508                     actiontype : this.cleanStyles[i],
47509                     html: 'Remove ' + this.cleanStyles[i],
47510                     handler: function(a,b) {
47511 //                        Roo.log(a);
47512 //                        Roo.log(b);
47513                         var c = Roo.get(editorcore.doc.body);
47514                         c.select('[style]').each(function(s) {
47515                             s.dom.style.removeProperty(a.actiontype);
47516                         });
47517                         editorcore.syncValue();
47518                     },
47519                     tabIndex:-1
47520                 });
47521             }
47522             cmenu.menu.items.push({
47523                 actiontype : 'tablewidths',
47524                 html: 'Remove Table Widths',
47525                 handler: function(a,b) {
47526                     editorcore.cleanTableWidths();
47527                     editorcore.syncValue();
47528                 },
47529                 tabIndex:-1
47530             });
47531             cmenu.menu.items.push({
47532                 actiontype : 'word',
47533                 html: 'Remove MS Word Formating',
47534                 handler: function(a,b) {
47535                     editorcore.cleanWord();
47536                     editorcore.syncValue();
47537                 },
47538                 tabIndex:-1
47539             });
47540             
47541             cmenu.menu.items.push({
47542                 actiontype : 'all',
47543                 html: 'Remove All Styles',
47544                 handler: function(a,b) {
47545                     
47546                     var c = Roo.get(editorcore.doc.body);
47547                     c.select('[style]').each(function(s) {
47548                         s.dom.removeAttribute('style');
47549                     });
47550                     editorcore.syncValue();
47551                 },
47552                 tabIndex:-1
47553             });
47554             
47555             cmenu.menu.items.push({
47556                 actiontype : 'all',
47557                 html: 'Remove All CSS Classes',
47558                 handler: function(a,b) {
47559                     
47560                     var c = Roo.get(editorcore.doc.body);
47561                     c.select('[class]').each(function(s) {
47562                         s.dom.removeAttribute('class');
47563                     });
47564                     editorcore.cleanWord();
47565                     editorcore.syncValue();
47566                 },
47567                 tabIndex:-1
47568             });
47569             
47570              cmenu.menu.items.push({
47571                 actiontype : 'tidy',
47572                 html: 'Tidy HTML Source',
47573                 handler: function(a,b) {
47574                     new Roo.htmleditor.Tidy(editorcore.doc.body);
47575                     editorcore.syncValue();
47576                 },
47577                 tabIndex:-1
47578             });
47579             
47580             
47581             tb.add(cmenu);
47582         }
47583          
47584         if (!this.disable.specialElements) {
47585             var semenu = {
47586                 text: "Other;",
47587                 cls: 'x-edit-none',
47588                 menu : {
47589                     items : []
47590                 }
47591             };
47592             for (var i =0; i < this.specialElements.length; i++) {
47593                 semenu.menu.items.push(
47594                     Roo.apply({ 
47595                         handler: function(a,b) {
47596                             editor.insertAtCursor(this.ihtml);
47597                         }
47598                     }, this.specialElements[i])
47599                 );
47600                     
47601             }
47602             
47603             tb.add(semenu);
47604             
47605             
47606         }
47607          
47608         
47609         if (this.btns) {
47610             for(var i =0; i< this.btns.length;i++) {
47611                 var b = Roo.factory(this.btns[i],Roo.form);
47612                 b.cls =  'x-edit-none';
47613                 
47614                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
47615                     b.cls += ' x-init-enable';
47616                 }
47617                 
47618                 b.scope = editorcore;
47619                 tb.add(b);
47620             }
47621         
47622         }
47623         
47624         
47625         
47626         // disable everything...
47627         
47628         this.tb.items.each(function(item){
47629             
47630            if(
47631                 item.id != editorcore.frameId+ '-sourceedit' && 
47632                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
47633             ){
47634                 
47635                 item.disable();
47636             }
47637         });
47638         this.rendered = true;
47639         
47640         // the all the btns;
47641         editor.on('editorevent', this.updateToolbar, this);
47642         // other toolbars need to implement this..
47643         //editor.on('editmodechange', this.updateToolbar, this);
47644     },
47645     
47646     
47647     relayBtnCmd : function(btn) {
47648         this.editorcore.relayCmd(btn.cmd);
47649     },
47650     // private used internally
47651     createLink : function(){
47652         Roo.log("create link?");
47653         var url = prompt(this.createLinkText, this.defaultLinkValue);
47654         if(url && url != 'http:/'+'/'){
47655             this.editorcore.relayCmd('createlink', url);
47656         }
47657     },
47658
47659     
47660     /**
47661      * Protected method that will not generally be called directly. It triggers
47662      * a toolbar update by reading the markup state of the current selection in the editor.
47663      */
47664     updateToolbar: function(){
47665
47666         if(!this.editorcore.activated){
47667             this.editor.onFirstFocus();
47668             return;
47669         }
47670
47671         var btns = this.tb.items.map, 
47672             doc = this.editorcore.doc,
47673             frameId = this.editorcore.frameId;
47674
47675         if(!this.disable.font && !Roo.isSafari){
47676             /*
47677             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
47678             if(name != this.fontSelect.dom.value){
47679                 this.fontSelect.dom.value = name;
47680             }
47681             */
47682         }
47683         if(!this.disable.format){
47684             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
47685             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
47686             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
47687             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
47688         }
47689         if(!this.disable.alignments){
47690             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
47691             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
47692             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
47693         }
47694         if(!Roo.isSafari && !this.disable.lists){
47695             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
47696             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
47697         }
47698         
47699         var ans = this.editorcore.getAllAncestors();
47700         if (this.formatCombo) {
47701             
47702             
47703             var store = this.formatCombo.store;
47704             this.formatCombo.setValue("");
47705             for (var i =0; i < ans.length;i++) {
47706                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
47707                     // select it..
47708                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
47709                     break;
47710                 }
47711             }
47712         }
47713         
47714         
47715         
47716         // hides menus... - so this cant be on a menu...
47717         Roo.menu.MenuMgr.hideAll();
47718
47719         //this.editorsyncValue();
47720     },
47721    
47722     
47723     createFontOptions : function(){
47724         var buf = [], fs = this.fontFamilies, ff, lc;
47725         
47726         
47727         
47728         for(var i = 0, len = fs.length; i< len; i++){
47729             ff = fs[i];
47730             lc = ff.toLowerCase();
47731             buf.push(
47732                 '<option value="',lc,'" style="font-family:',ff,';"',
47733                     (this.defaultFont == lc ? ' selected="true">' : '>'),
47734                     ff,
47735                 '</option>'
47736             );
47737         }
47738         return buf.join('');
47739     },
47740     
47741     toggleSourceEdit : function(sourceEditMode){
47742         
47743         Roo.log("toolbar toogle");
47744         if(sourceEditMode === undefined){
47745             sourceEditMode = !this.sourceEditMode;
47746         }
47747         this.sourceEditMode = sourceEditMode === true;
47748         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
47749         // just toggle the button?
47750         if(btn.pressed !== this.sourceEditMode){
47751             btn.toggle(this.sourceEditMode);
47752             return;
47753         }
47754         
47755         if(sourceEditMode){
47756             Roo.log("disabling buttons");
47757             this.tb.items.each(function(item){
47758                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
47759                     item.disable();
47760                 }
47761             });
47762           
47763         }else{
47764             Roo.log("enabling buttons");
47765             if(this.editorcore.initialized){
47766                 this.tb.items.each(function(item){
47767                     item.enable();
47768                 });
47769             }
47770             
47771         }
47772         Roo.log("calling toggole on editor");
47773         // tell the editor that it's been pressed..
47774         this.editor.toggleSourceEdit(sourceEditMode);
47775        
47776     },
47777      /**
47778      * Object collection of toolbar tooltips for the buttons in the editor. The key
47779      * is the command id associated with that button and the value is a valid QuickTips object.
47780      * For example:
47781 <pre><code>
47782 {
47783     bold : {
47784         title: 'Bold (Ctrl+B)',
47785         text: 'Make the selected text bold.',
47786         cls: 'x-html-editor-tip'
47787     },
47788     italic : {
47789         title: 'Italic (Ctrl+I)',
47790         text: 'Make the selected text italic.',
47791         cls: 'x-html-editor-tip'
47792     },
47793     ...
47794 </code></pre>
47795     * @type Object
47796      */
47797     buttonTips : {
47798         bold : {
47799             title: 'Bold (Ctrl+B)',
47800             text: 'Make the selected text bold.',
47801             cls: 'x-html-editor-tip'
47802         },
47803         italic : {
47804             title: 'Italic (Ctrl+I)',
47805             text: 'Make the selected text italic.',
47806             cls: 'x-html-editor-tip'
47807         },
47808         underline : {
47809             title: 'Underline (Ctrl+U)',
47810             text: 'Underline the selected text.',
47811             cls: 'x-html-editor-tip'
47812         },
47813         strikethrough : {
47814             title: 'Strikethrough',
47815             text: 'Strikethrough the selected text.',
47816             cls: 'x-html-editor-tip'
47817         },
47818         increasefontsize : {
47819             title: 'Grow Text',
47820             text: 'Increase the font size.',
47821             cls: 'x-html-editor-tip'
47822         },
47823         decreasefontsize : {
47824             title: 'Shrink Text',
47825             text: 'Decrease the font size.',
47826             cls: 'x-html-editor-tip'
47827         },
47828         backcolor : {
47829             title: 'Text Highlight Color',
47830             text: 'Change the background color of the selected text.',
47831             cls: 'x-html-editor-tip'
47832         },
47833         forecolor : {
47834             title: 'Font Color',
47835             text: 'Change the color of the selected text.',
47836             cls: 'x-html-editor-tip'
47837         },
47838         justifyleft : {
47839             title: 'Align Text Left',
47840             text: 'Align text to the left.',
47841             cls: 'x-html-editor-tip'
47842         },
47843         justifycenter : {
47844             title: 'Center Text',
47845             text: 'Center text in the editor.',
47846             cls: 'x-html-editor-tip'
47847         },
47848         justifyright : {
47849             title: 'Align Text Right',
47850             text: 'Align text to the right.',
47851             cls: 'x-html-editor-tip'
47852         },
47853         insertunorderedlist : {
47854             title: 'Bullet List',
47855             text: 'Start a bulleted list.',
47856             cls: 'x-html-editor-tip'
47857         },
47858         insertorderedlist : {
47859             title: 'Numbered List',
47860             text: 'Start a numbered list.',
47861             cls: 'x-html-editor-tip'
47862         },
47863         createlink : {
47864             title: 'Hyperlink',
47865             text: 'Make the selected text a hyperlink.',
47866             cls: 'x-html-editor-tip'
47867         },
47868         sourceedit : {
47869             title: 'Source Edit',
47870             text: 'Switch to source editing mode.',
47871             cls: 'x-html-editor-tip'
47872         }
47873     },
47874     // private
47875     onDestroy : function(){
47876         if(this.rendered){
47877             
47878             this.tb.items.each(function(item){
47879                 if(item.menu){
47880                     item.menu.removeAll();
47881                     if(item.menu.el){
47882                         item.menu.el.destroy();
47883                     }
47884                 }
47885                 item.destroy();
47886             });
47887              
47888         }
47889     },
47890     onFirstFocus: function() {
47891         this.tb.items.each(function(item){
47892            item.enable();
47893         });
47894     }
47895 });
47896
47897
47898
47899
47900 // <script type="text/javascript">
47901 /*
47902  * Based on
47903  * Ext JS Library 1.1.1
47904  * Copyright(c) 2006-2007, Ext JS, LLC.
47905  *  
47906  
47907  */
47908
47909  
47910 /**
47911  * @class Roo.form.HtmlEditor.ToolbarContext
47912  * Context Toolbar
47913  * 
47914  * Usage:
47915  *
47916  new Roo.form.HtmlEditor({
47917     ....
47918     toolbars : [
47919         { xtype: 'ToolbarStandard', styles : {} }
47920         { xtype: 'ToolbarContext', disable : {} }
47921     ]
47922 })
47923
47924      
47925  * 
47926  * @config : {Object} disable List of elements to disable.. (not done yet.)
47927  * @config : {Object} styles  Map of styles available.
47928  * 
47929  */
47930
47931 Roo.form.HtmlEditor.ToolbarContext = function(config)
47932 {
47933     
47934     Roo.apply(this, config);
47935     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47936     // dont call parent... till later.
47937     this.styles = this.styles || {};
47938 }
47939
47940  
47941
47942 Roo.form.HtmlEditor.ToolbarContext.types = {
47943     'IMG' : {
47944         width : {
47945             title: "Width",
47946             width: 40
47947         },
47948         height:  {
47949             title: "Height",
47950             width: 40
47951         },
47952         align: {
47953             title: "Align",
47954             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
47955             width : 80
47956             
47957         },
47958         border: {
47959             title: "Border",
47960             width: 40
47961         },
47962         alt: {
47963             title: "Alt",
47964             width: 120
47965         },
47966         src : {
47967             title: "Src",
47968             width: 220
47969         }
47970         
47971     },
47972     
47973     'FIGURE' : {
47974          align: {
47975             title: "Align",
47976             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
47977             width : 80  
47978         }
47979     },
47980     'A' : {
47981         name : {
47982             title: "Name",
47983             width: 50
47984         },
47985         target:  {
47986             title: "Target",
47987             width: 120
47988         },
47989         href:  {
47990             title: "Href",
47991             width: 220
47992         } // border?
47993         
47994     },
47995     'TABLE' : {
47996         rows : {
47997             title: "Rows",
47998             width: 20
47999         },
48000         cols : {
48001             title: "Cols",
48002             width: 20
48003         },
48004         width : {
48005             title: "Width",
48006             width: 40
48007         },
48008         height : {
48009             title: "Height",
48010             width: 40
48011         },
48012         border : {
48013             title: "Border",
48014             width: 20
48015         }
48016     },
48017     'TD' : {
48018         width : {
48019             title: "Width",
48020             width: 40
48021         },
48022         height : {
48023             title: "Height",
48024             width: 40
48025         },   
48026         align: {
48027             title: "Align",
48028             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
48029             width: 80
48030         },
48031         valign: {
48032             title: "Valign",
48033             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
48034             width: 80
48035         },
48036         colspan: {
48037             title: "Colspan",
48038             width: 20
48039             
48040         },
48041          'font-family'  : {
48042             title : "Font",
48043             style : 'fontFamily',
48044             displayField: 'display',
48045             optname : 'font-family',
48046             width: 140
48047         }
48048     },
48049     'INPUT' : {
48050         name : {
48051             title: "name",
48052             width: 120
48053         },
48054         value : {
48055             title: "Value",
48056             width: 120
48057         },
48058         width : {
48059             title: "Width",
48060             width: 40
48061         }
48062     },
48063     'LABEL' : {
48064         'for' : {
48065             title: "For",
48066             width: 120
48067         }
48068     },
48069     'TEXTAREA' : {
48070           name : {
48071             title: "name",
48072             width: 120
48073         },
48074         rows : {
48075             title: "Rows",
48076             width: 20
48077         },
48078         cols : {
48079             title: "Cols",
48080             width: 20
48081         }
48082     },
48083     'SELECT' : {
48084         name : {
48085             title: "name",
48086             width: 120
48087         },
48088         selectoptions : {
48089             title: "Options",
48090             width: 200
48091         }
48092     },
48093     
48094     // should we really allow this??
48095     // should this just be 
48096     'BODY' : {
48097         title : {
48098             title: "Title",
48099             width: 200,
48100             disabled : true
48101         }
48102     },
48103     'SPAN' : {
48104         'font-family'  : {
48105             title : "Font",
48106             style : 'fontFamily',
48107             displayField: 'display',
48108             optname : 'font-family',
48109             width: 140
48110         }
48111     },
48112     'DIV' : {
48113         'font-family'  : {
48114             title : "Font",
48115             style : 'fontFamily',
48116             displayField: 'display',
48117             optname : 'font-family',
48118             width: 140
48119         }
48120     },
48121      'P' : {
48122         'font-family'  : {
48123             title : "Font",
48124             style : 'fontFamily',
48125             displayField: 'display',
48126             optname : 'font-family',
48127             width: 140
48128         }
48129     },
48130     
48131     '*' : {
48132         // empty..
48133     }
48134
48135 };
48136
48137 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
48138 Roo.form.HtmlEditor.ToolbarContext.stores = false;
48139
48140 Roo.form.HtmlEditor.ToolbarContext.options = {
48141         'font-family'  : [ 
48142                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
48143                 [ 'Courier New', 'Courier New'],
48144                 [ 'Tahoma', 'Tahoma'],
48145                 [ 'Times New Roman,serif', 'Times'],
48146                 [ 'Verdana','Verdana' ]
48147         ]
48148 };
48149
48150 // fixme - these need to be configurable..
48151  
48152
48153 //Roo.form.HtmlEditor.ToolbarContext.types
48154
48155
48156 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
48157     
48158     tb: false,
48159     
48160     rendered: false,
48161     
48162     editor : false,
48163     editorcore : false,
48164     /**
48165      * @cfg {Object} disable  List of toolbar elements to disable
48166          
48167      */
48168     disable : false,
48169     /**
48170      * @cfg {Object} styles List of styles 
48171      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
48172      *
48173      * These must be defined in the page, so they get rendered correctly..
48174      * .headline { }
48175      * TD.underline { }
48176      * 
48177      */
48178     styles : false,
48179     
48180     options: false,
48181     
48182     toolbars : false,
48183     
48184     init : function(editor)
48185     {
48186         this.editor = editor;
48187         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48188         var editorcore = this.editorcore;
48189         
48190         var fid = editorcore.frameId;
48191         var etb = this;
48192         function btn(id, toggle, handler){
48193             var xid = fid + '-'+ id ;
48194             return {
48195                 id : xid,
48196                 cmd : id,
48197                 cls : 'x-btn-icon x-edit-'+id,
48198                 enableToggle:toggle !== false,
48199                 scope: editorcore, // was editor...
48200                 handler:handler||editorcore.relayBtnCmd,
48201                 clickEvent:'mousedown',
48202                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48203                 tabIndex:-1
48204             };
48205         }
48206         // create a new element.
48207         var wdiv = editor.wrap.createChild({
48208                 tag: 'div'
48209             }, editor.wrap.dom.firstChild.nextSibling, true);
48210         
48211         // can we do this more than once??
48212         
48213          // stop form submits
48214       
48215  
48216         // disable everything...
48217         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48218         this.toolbars = {};
48219            
48220         for (var i in  ty) {
48221           
48222             this.toolbars[i] = this.buildToolbar(ty[i],i);
48223         }
48224         this.tb = this.toolbars.BODY;
48225         this.tb.el.show();
48226         this.buildFooter();
48227         this.footer.show();
48228         editor.on('hide', function( ) { this.footer.hide() }, this);
48229         editor.on('show', function( ) { this.footer.show() }, this);
48230         
48231          
48232         this.rendered = true;
48233         
48234         // the all the btns;
48235         editor.on('editorevent', this.updateToolbar, this);
48236         // other toolbars need to implement this..
48237         //editor.on('editmodechange', this.updateToolbar, this);
48238     },
48239     
48240     
48241     
48242     /**
48243      * Protected method that will not generally be called directly. It triggers
48244      * a toolbar update by reading the markup state of the current selection in the editor.
48245      *
48246      * Note you can force an update by calling on('editorevent', scope, false)
48247      */
48248     updateToolbar: function(editor,ev,sel){
48249
48250         //Roo.log(ev);
48251         // capture mouse up - this is handy for selecting images..
48252         // perhaps should go somewhere else...
48253         if(!this.editorcore.activated){
48254              this.editor.onFirstFocus();
48255             return;
48256         }
48257         
48258         
48259         
48260         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
48261         // selectNode - might want to handle IE?
48262         if (ev &&
48263             (ev.type == 'mouseup' || ev.type == 'click' ) &&
48264             ev.target && ev.target.tagName == 'IMG') {
48265             // they have click on an image...
48266             // let's see if we can change the selection...
48267             sel = ev.target;
48268          
48269               var nodeRange = sel.ownerDocument.createRange();
48270             try {
48271                 nodeRange.selectNode(sel);
48272             } catch (e) {
48273                 nodeRange.selectNodeContents(sel);
48274             }
48275             //nodeRange.collapse(true);
48276             var s = this.editorcore.win.getSelection();
48277             s.removeAllRanges();
48278             s.addRange(nodeRange);
48279         }  
48280         
48281       
48282         var updateFooter = sel ? false : true;
48283         
48284         
48285         var ans = this.editorcore.getAllAncestors();
48286         
48287         // pick
48288         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48289         
48290         if (!sel) { 
48291             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
48292             sel = sel ? sel : this.editorcore.doc.body;
48293             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
48294             
48295         }
48296         // pick a menu that exists..
48297         var tn = sel.tagName.toUpperCase();
48298         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
48299         
48300         tn = sel.tagName.toUpperCase();
48301         
48302         var lastSel = this.tb.selectedNode;
48303         
48304         this.tb.selectedNode = sel;
48305         
48306         // if current menu does not match..
48307         
48308         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
48309                 
48310             this.tb.el.hide();
48311             ///console.log("show: " + tn);
48312             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
48313             this.tb.el.show();
48314             // update name
48315             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
48316             
48317             
48318             // update attributes
48319             if (this.tb.fields) {
48320                 this.tb.fields.each(function(e) {
48321                     if (e.stylename) {
48322                         e.setValue(sel.style[e.stylename]);
48323                         return;
48324                     } 
48325                    e.setValue(sel.getAttribute(e.attrname));
48326                 });
48327             }
48328             
48329             var hasStyles = false;
48330             for(var i in this.styles) {
48331                 hasStyles = true;
48332                 break;
48333             }
48334             
48335             // update styles
48336             if (hasStyles) { 
48337                 var st = this.tb.fields.item(0);
48338                 
48339                 st.store.removeAll();
48340                
48341                 
48342                 var cn = sel.className.split(/\s+/);
48343                 
48344                 var avs = [];
48345                 if (this.styles['*']) {
48346                     
48347                     Roo.each(this.styles['*'], function(v) {
48348                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48349                     });
48350                 }
48351                 if (this.styles[tn]) { 
48352                     Roo.each(this.styles[tn], function(v) {
48353                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48354                     });
48355                 }
48356                 
48357                 st.store.loadData(avs);
48358                 st.collapse();
48359                 st.setValue(cn);
48360             }
48361             // flag our selected Node.
48362             this.tb.selectedNode = sel;
48363            
48364            
48365             Roo.menu.MenuMgr.hideAll();
48366
48367         }
48368         
48369         if (!updateFooter) {
48370             //this.footDisp.dom.innerHTML = ''; 
48371             return;
48372         }
48373         // update the footer
48374         //
48375         var html = '';
48376         
48377         this.footerEls = ans.reverse();
48378         Roo.each(this.footerEls, function(a,i) {
48379             if (!a) { return; }
48380             html += html.length ? ' &gt; '  :  '';
48381             
48382             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48383             
48384         });
48385        
48386         // 
48387         var sz = this.footDisp.up('td').getSize();
48388         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48389         this.footDisp.dom.style.marginLeft = '5px';
48390         
48391         this.footDisp.dom.style.overflow = 'hidden';
48392         
48393         this.footDisp.dom.innerHTML = html;
48394             
48395         //this.editorsyncValue();
48396     },
48397      
48398     
48399    
48400        
48401     // private
48402     onDestroy : function(){
48403         if(this.rendered){
48404             
48405             this.tb.items.each(function(item){
48406                 if(item.menu){
48407                     item.menu.removeAll();
48408                     if(item.menu.el){
48409                         item.menu.el.destroy();
48410                     }
48411                 }
48412                 item.destroy();
48413             });
48414              
48415         }
48416     },
48417     onFirstFocus: function() {
48418         // need to do this for all the toolbars..
48419         this.tb.items.each(function(item){
48420            item.enable();
48421         });
48422     },
48423     buildToolbar: function(tlist, nm)
48424     {
48425         var editor = this.editor;
48426         var editorcore = this.editorcore;
48427          // create a new element.
48428         var wdiv = editor.wrap.createChild({
48429                 tag: 'div'
48430             }, editor.wrap.dom.firstChild.nextSibling, true);
48431         
48432        
48433         var tb = new Roo.Toolbar(wdiv);
48434         // add the name..
48435         
48436         tb.add(nm+ ":&nbsp;");
48437         
48438         var styles = [];
48439         for(var i in this.styles) {
48440             styles.push(i);
48441         }
48442         
48443         // styles...
48444         if (styles && styles.length) {
48445             
48446             // this needs a multi-select checkbox...
48447             tb.addField( new Roo.form.ComboBox({
48448                 store: new Roo.data.SimpleStore({
48449                     id : 'val',
48450                     fields: ['val', 'selected'],
48451                     data : [] 
48452                 }),
48453                 name : '-roo-edit-className',
48454                 attrname : 'className',
48455                 displayField: 'val',
48456                 typeAhead: false,
48457                 mode: 'local',
48458                 editable : false,
48459                 triggerAction: 'all',
48460                 emptyText:'Select Style',
48461                 selectOnFocus:true,
48462                 width: 130,
48463                 listeners : {
48464                     'select': function(c, r, i) {
48465                         // initial support only for on class per el..
48466                         tb.selectedNode.className =  r ? r.get('val') : '';
48467                         editorcore.syncValue();
48468                     }
48469                 }
48470     
48471             }));
48472         }
48473         
48474         var tbc = Roo.form.HtmlEditor.ToolbarContext;
48475         var tbops = tbc.options;
48476         
48477         for (var i in tlist) {
48478             
48479             var item = tlist[i];
48480             tb.add(item.title + ":&nbsp;");
48481             
48482             
48483             //optname == used so you can configure the options available..
48484             var opts = item.opts ? item.opts : false;
48485             if (item.optname) {
48486                 opts = tbops[item.optname];
48487            
48488             }
48489             
48490             if (opts) {
48491                 // opts == pulldown..
48492                 tb.addField( new Roo.form.ComboBox({
48493                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
48494                         id : 'val',
48495                         fields: ['val', 'display'],
48496                         data : opts  
48497                     }),
48498                     name : '-roo-edit-' + i,
48499                     attrname : i,
48500                     stylename : item.style ? item.style : false,
48501                     displayField: item.displayField ? item.displayField : 'val',
48502                     valueField :  'val',
48503                     typeAhead: false,
48504                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
48505                     editable : false,
48506                     triggerAction: 'all',
48507                     emptyText:'Select',
48508                     selectOnFocus:true,
48509                     width: item.width ? item.width  : 130,
48510                     listeners : {
48511                         'select': function(c, r, i) {
48512                             if (c.stylename) {
48513                                 tb.selectedNode.style[c.stylename] =  r.get('val');
48514                                 return;
48515                             }
48516                             if (r === false) {
48517                                 tb.selectedNode.removeAttribute(c.attrname);
48518                                 return;
48519                             }
48520                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
48521                         }
48522                     }
48523
48524                 }));
48525                 continue;
48526                     
48527                  
48528                 
48529                 tb.addField( new Roo.form.TextField({
48530                     name: i,
48531                     width: 100,
48532                     //allowBlank:false,
48533                     value: ''
48534                 }));
48535                 continue;
48536             }
48537             tb.addField( new Roo.form.TextField({
48538                 name: '-roo-edit-' + i,
48539                 attrname : i,
48540                 
48541                 width: item.width,
48542                 //allowBlank:true,
48543                 value: '',
48544                 listeners: {
48545                     'change' : function(f, nv, ov) {
48546                         tb.selectedNode.setAttribute(f.attrname, nv);
48547                         editorcore.syncValue();
48548                     }
48549                 }
48550             }));
48551              
48552         }
48553         
48554         var _this = this;
48555         
48556         if(nm == 'BODY'){
48557             tb.addSeparator();
48558         
48559             tb.addButton( {
48560                 text: 'Stylesheets',
48561
48562                 listeners : {
48563                     click : function ()
48564                     {
48565                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
48566                     }
48567                 }
48568             });
48569         }
48570         
48571         tb.addFill();
48572         tb.addButton( {
48573             text: 'Remove Tag',
48574     
48575             listeners : {
48576                 click : function ()
48577                 {
48578                     // remove
48579                     // undo does not work.
48580                      
48581                     var sn = tb.selectedNode;
48582                     
48583                     var pn = sn.parentNode;
48584                     
48585                     var stn =  sn.childNodes[0];
48586                     var en = sn.childNodes[sn.childNodes.length - 1 ];
48587                     while (sn.childNodes.length) {
48588                         var node = sn.childNodes[0];
48589                         sn.removeChild(node);
48590                         //Roo.log(node);
48591                         pn.insertBefore(node, sn);
48592                         
48593                     }
48594                     pn.removeChild(sn);
48595                     var range = editorcore.createRange();
48596         
48597                     range.setStart(stn,0);
48598                     range.setEnd(en,0); //????
48599                     //range.selectNode(sel);
48600                     
48601                     
48602                     var selection = editorcore.getSelection();
48603                     selection.removeAllRanges();
48604                     selection.addRange(range);
48605                     
48606                     
48607                     
48608                     //_this.updateToolbar(null, null, pn);
48609                     _this.updateToolbar(null, null, null);
48610                     _this.footDisp.dom.innerHTML = ''; 
48611                 }
48612             }
48613             
48614                     
48615                 
48616             
48617         });
48618         
48619         
48620         tb.el.on('click', function(e){
48621             e.preventDefault(); // what does this do?
48622         });
48623         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
48624         tb.el.hide();
48625         tb.name = nm;
48626         // dont need to disable them... as they will get hidden
48627         return tb;
48628          
48629         
48630     },
48631     buildFooter : function()
48632     {
48633         
48634         var fel = this.editor.wrap.createChild();
48635         this.footer = new Roo.Toolbar(fel);
48636         // toolbar has scrolly on left / right?
48637         var footDisp= new Roo.Toolbar.Fill();
48638         var _t = this;
48639         this.footer.add(
48640             {
48641                 text : '&lt;',
48642                 xtype: 'Button',
48643                 handler : function() {
48644                     _t.footDisp.scrollTo('left',0,true)
48645                 }
48646             }
48647         );
48648         this.footer.add( footDisp );
48649         this.footer.add( 
48650             {
48651                 text : '&gt;',
48652                 xtype: 'Button',
48653                 handler : function() {
48654                     // no animation..
48655                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
48656                 }
48657             }
48658         );
48659         var fel = Roo.get(footDisp.el);
48660         fel.addClass('x-editor-context');
48661         this.footDispWrap = fel; 
48662         this.footDispWrap.overflow  = 'hidden';
48663         
48664         this.footDisp = fel.createChild();
48665         this.footDispWrap.on('click', this.onContextClick, this)
48666         
48667         
48668     },
48669     onContextClick : function (ev,dom)
48670     {
48671         ev.preventDefault();
48672         var  cn = dom.className;
48673         //Roo.log(cn);
48674         if (!cn.match(/x-ed-loc-/)) {
48675             return;
48676         }
48677         var n = cn.split('-').pop();
48678         var ans = this.footerEls;
48679         var sel = ans[n];
48680         
48681          // pick
48682         var range = this.editorcore.createRange();
48683         
48684         range.selectNodeContents(sel);
48685         //range.selectNode(sel);
48686         
48687         
48688         var selection = this.editorcore.getSelection();
48689         selection.removeAllRanges();
48690         selection.addRange(range);
48691         
48692         
48693         
48694         this.updateToolbar(null, null, sel);
48695         
48696         
48697     }
48698     
48699     
48700     
48701     
48702     
48703 });
48704
48705
48706
48707
48708
48709 /*
48710  * Based on:
48711  * Ext JS Library 1.1.1
48712  * Copyright(c) 2006-2007, Ext JS, LLC.
48713  *
48714  * Originally Released Under LGPL - original licence link has changed is not relivant.
48715  *
48716  * Fork - LGPL
48717  * <script type="text/javascript">
48718  */
48719  
48720 /**
48721  * @class Roo.form.BasicForm
48722  * @extends Roo.util.Observable
48723  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
48724  * @constructor
48725  * @param {String/HTMLElement/Roo.Element} el The form element or its id
48726  * @param {Object} config Configuration options
48727  */
48728 Roo.form.BasicForm = function(el, config){
48729     this.allItems = [];
48730     this.childForms = [];
48731     Roo.apply(this, config);
48732     /*
48733      * The Roo.form.Field items in this form.
48734      * @type MixedCollection
48735      */
48736      
48737      
48738     this.items = new Roo.util.MixedCollection(false, function(o){
48739         return o.id || (o.id = Roo.id());
48740     });
48741     this.addEvents({
48742         /**
48743          * @event beforeaction
48744          * Fires before any action is performed. Return false to cancel the action.
48745          * @param {Form} this
48746          * @param {Action} action The action to be performed
48747          */
48748         beforeaction: true,
48749         /**
48750          * @event actionfailed
48751          * Fires when an action fails.
48752          * @param {Form} this
48753          * @param {Action} action The action that failed
48754          */
48755         actionfailed : true,
48756         /**
48757          * @event actioncomplete
48758          * Fires when an action is completed.
48759          * @param {Form} this
48760          * @param {Action} action The action that completed
48761          */
48762         actioncomplete : true
48763     });
48764     if(el){
48765         this.initEl(el);
48766     }
48767     Roo.form.BasicForm.superclass.constructor.call(this);
48768     
48769     Roo.form.BasicForm.popover.apply();
48770 };
48771
48772 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
48773     /**
48774      * @cfg {String} method
48775      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
48776      */
48777     /**
48778      * @cfg {DataReader} reader
48779      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
48780      * This is optional as there is built-in support for processing JSON.
48781      */
48782     /**
48783      * @cfg {DataReader} errorReader
48784      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
48785      * This is completely optional as there is built-in support for processing JSON.
48786      */
48787     /**
48788      * @cfg {String} url
48789      * The URL to use for form actions if one isn't supplied in the action options.
48790      */
48791     /**
48792      * @cfg {Boolean} fileUpload
48793      * Set to true if this form is a file upload.
48794      */
48795      
48796     /**
48797      * @cfg {Object} baseParams
48798      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
48799      */
48800      /**
48801      
48802     /**
48803      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
48804      */
48805     timeout: 30,
48806
48807     // private
48808     activeAction : null,
48809
48810     /**
48811      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
48812      * or setValues() data instead of when the form was first created.
48813      */
48814     trackResetOnLoad : false,
48815     
48816     
48817     /**
48818      * childForms - used for multi-tab forms
48819      * @type {Array}
48820      */
48821     childForms : false,
48822     
48823     /**
48824      * allItems - full list of fields.
48825      * @type {Array}
48826      */
48827     allItems : false,
48828     
48829     /**
48830      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
48831      * element by passing it or its id or mask the form itself by passing in true.
48832      * @type Mixed
48833      */
48834     waitMsgTarget : false,
48835     
48836     /**
48837      * @type Boolean
48838      */
48839     disableMask : false,
48840     
48841     /**
48842      * @cfg {Boolean} errorMask (true|false) default false
48843      */
48844     errorMask : false,
48845     
48846     /**
48847      * @cfg {Number} maskOffset Default 100
48848      */
48849     maskOffset : 100,
48850
48851     // private
48852     initEl : function(el){
48853         this.el = Roo.get(el);
48854         this.id = this.el.id || Roo.id();
48855         this.el.on('submit', this.onSubmit, this);
48856         this.el.addClass('x-form');
48857     },
48858
48859     // private
48860     onSubmit : function(e){
48861         e.stopEvent();
48862     },
48863
48864     /**
48865      * Returns true if client-side validation on the form is successful.
48866      * @return Boolean
48867      */
48868     isValid : function(){
48869         var valid = true;
48870         var target = false;
48871         this.items.each(function(f){
48872             if(f.validate()){
48873                 return;
48874             }
48875             
48876             valid = false;
48877                 
48878             if(!target && f.el.isVisible(true)){
48879                 target = f;
48880             }
48881         });
48882         
48883         if(this.errorMask && !valid){
48884             Roo.form.BasicForm.popover.mask(this, target);
48885         }
48886         
48887         return valid;
48888     },
48889     /**
48890      * Returns array of invalid form fields.
48891      * @return Array
48892      */
48893     
48894     invalidFields : function()
48895     {
48896         var ret = [];
48897         this.items.each(function(f){
48898             if(f.validate()){
48899                 return;
48900             }
48901             ret.push(f);
48902             
48903         });
48904         
48905         return ret;
48906     },
48907     
48908     
48909     /**
48910      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
48911      * @return Boolean
48912      */
48913     isDirty : function(){
48914         var dirty = false;
48915         this.items.each(function(f){
48916            if(f.isDirty()){
48917                dirty = true;
48918                return false;
48919            }
48920         });
48921         return dirty;
48922     },
48923     
48924     /**
48925      * Returns true if any fields in this form have changed since their original load. (New version)
48926      * @return Boolean
48927      */
48928     
48929     hasChanged : function()
48930     {
48931         var dirty = false;
48932         this.items.each(function(f){
48933            if(f.hasChanged()){
48934                dirty = true;
48935                return false;
48936            }
48937         });
48938         return dirty;
48939         
48940     },
48941     /**
48942      * Resets all hasChanged to 'false' -
48943      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
48944      * So hasChanged storage is only to be used for this purpose
48945      * @return Boolean
48946      */
48947     resetHasChanged : function()
48948     {
48949         this.items.each(function(f){
48950            f.resetHasChanged();
48951         });
48952         
48953     },
48954     
48955     
48956     /**
48957      * Performs a predefined action (submit or load) or custom actions you define on this form.
48958      * @param {String} actionName The name of the action type
48959      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
48960      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
48961      * accept other config options):
48962      * <pre>
48963 Property          Type             Description
48964 ----------------  ---------------  ----------------------------------------------------------------------------------
48965 url               String           The url for the action (defaults to the form's url)
48966 method            String           The form method to use (defaults to the form's method, or POST if not defined)
48967 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
48968 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
48969                                    validate the form on the client (defaults to false)
48970      * </pre>
48971      * @return {BasicForm} this
48972      */
48973     doAction : function(action, options){
48974         if(typeof action == 'string'){
48975             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
48976         }
48977         if(this.fireEvent('beforeaction', this, action) !== false){
48978             this.beforeAction(action);
48979             action.run.defer(100, action);
48980         }
48981         return this;
48982     },
48983
48984     /**
48985      * Shortcut to do a submit action.
48986      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48987      * @return {BasicForm} this
48988      */
48989     submit : function(options){
48990         this.doAction('submit', options);
48991         return this;
48992     },
48993
48994     /**
48995      * Shortcut to do a load action.
48996      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48997      * @return {BasicForm} this
48998      */
48999     load : function(options){
49000         this.doAction('load', options);
49001         return this;
49002     },
49003
49004     /**
49005      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
49006      * @param {Record} record The record to edit
49007      * @return {BasicForm} this
49008      */
49009     updateRecord : function(record){
49010         record.beginEdit();
49011         var fs = record.fields;
49012         fs.each(function(f){
49013             var field = this.findField(f.name);
49014             if(field){
49015                 record.set(f.name, field.getValue());
49016             }
49017         }, this);
49018         record.endEdit();
49019         return this;
49020     },
49021
49022     /**
49023      * Loads an Roo.data.Record into this form.
49024      * @param {Record} record The record to load
49025      * @return {BasicForm} this
49026      */
49027     loadRecord : function(record){
49028         this.setValues(record.data);
49029         return this;
49030     },
49031
49032     // private
49033     beforeAction : function(action){
49034         var o = action.options;
49035         
49036         if(!this.disableMask) {
49037             if(this.waitMsgTarget === true){
49038                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
49039             }else if(this.waitMsgTarget){
49040                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
49041                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
49042             }else {
49043                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
49044             }
49045         }
49046         
49047          
49048     },
49049
49050     // private
49051     afterAction : function(action, success){
49052         this.activeAction = null;
49053         var o = action.options;
49054         
49055         if(!this.disableMask) {
49056             if(this.waitMsgTarget === true){
49057                 this.el.unmask();
49058             }else if(this.waitMsgTarget){
49059                 this.waitMsgTarget.unmask();
49060             }else{
49061                 Roo.MessageBox.updateProgress(1);
49062                 Roo.MessageBox.hide();
49063             }
49064         }
49065         
49066         if(success){
49067             if(o.reset){
49068                 this.reset();
49069             }
49070             Roo.callback(o.success, o.scope, [this, action]);
49071             this.fireEvent('actioncomplete', this, action);
49072             
49073         }else{
49074             
49075             // failure condition..
49076             // we have a scenario where updates need confirming.
49077             // eg. if a locking scenario exists..
49078             // we look for { errors : { needs_confirm : true }} in the response.
49079             if (
49080                 (typeof(action.result) != 'undefined')  &&
49081                 (typeof(action.result.errors) != 'undefined')  &&
49082                 (typeof(action.result.errors.needs_confirm) != 'undefined')
49083            ){
49084                 var _t = this;
49085                 Roo.MessageBox.confirm(
49086                     "Change requires confirmation",
49087                     action.result.errorMsg,
49088                     function(r) {
49089                         if (r != 'yes') {
49090                             return;
49091                         }
49092                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
49093                     }
49094                     
49095                 );
49096                 
49097                 
49098                 
49099                 return;
49100             }
49101             
49102             Roo.callback(o.failure, o.scope, [this, action]);
49103             // show an error message if no failed handler is set..
49104             if (!this.hasListener('actionfailed')) {
49105                 Roo.MessageBox.alert("Error",
49106                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
49107                         action.result.errorMsg :
49108                         "Saving Failed, please check your entries or try again"
49109                 );
49110             }
49111             
49112             this.fireEvent('actionfailed', this, action);
49113         }
49114         
49115     },
49116
49117     /**
49118      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
49119      * @param {String} id The value to search for
49120      * @return Field
49121      */
49122     findField : function(id){
49123         var field = this.items.get(id);
49124         if(!field){
49125             this.items.each(function(f){
49126                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
49127                     field = f;
49128                     return false;
49129                 }
49130             });
49131         }
49132         return field || null;
49133     },
49134
49135     /**
49136      * Add a secondary form to this one, 
49137      * Used to provide tabbed forms. One form is primary, with hidden values 
49138      * which mirror the elements from the other forms.
49139      * 
49140      * @param {Roo.form.Form} form to add.
49141      * 
49142      */
49143     addForm : function(form)
49144     {
49145        
49146         if (this.childForms.indexOf(form) > -1) {
49147             // already added..
49148             return;
49149         }
49150         this.childForms.push(form);
49151         var n = '';
49152         Roo.each(form.allItems, function (fe) {
49153             
49154             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
49155             if (this.findField(n)) { // already added..
49156                 return;
49157             }
49158             var add = new Roo.form.Hidden({
49159                 name : n
49160             });
49161             add.render(this.el);
49162             
49163             this.add( add );
49164         }, this);
49165         
49166     },
49167     /**
49168      * Mark fields in this form invalid in bulk.
49169      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
49170      * @return {BasicForm} this
49171      */
49172     markInvalid : function(errors){
49173         if(errors instanceof Array){
49174             for(var i = 0, len = errors.length; i < len; i++){
49175                 var fieldError = errors[i];
49176                 var f = this.findField(fieldError.id);
49177                 if(f){
49178                     f.markInvalid(fieldError.msg);
49179                 }
49180             }
49181         }else{
49182             var field, id;
49183             for(id in errors){
49184                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
49185                     field.markInvalid(errors[id]);
49186                 }
49187             }
49188         }
49189         Roo.each(this.childForms || [], function (f) {
49190             f.markInvalid(errors);
49191         });
49192         
49193         return this;
49194     },
49195
49196     /**
49197      * Set values for fields in this form in bulk.
49198      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
49199      * @return {BasicForm} this
49200      */
49201     setValues : function(values){
49202         if(values instanceof Array){ // array of objects
49203             for(var i = 0, len = values.length; i < len; i++){
49204                 var v = values[i];
49205                 var f = this.findField(v.id);
49206                 if(f){
49207                     f.setValue(v.value);
49208                     if(this.trackResetOnLoad){
49209                         f.originalValue = f.getValue();
49210                     }
49211                 }
49212             }
49213         }else{ // object hash
49214             var field, id;
49215             for(id in values){
49216                 if(typeof values[id] != 'function' && (field = this.findField(id))){
49217                     
49218                     if (field.setFromData && 
49219                         field.valueField && 
49220                         field.displayField &&
49221                         // combos' with local stores can 
49222                         // be queried via setValue()
49223                         // to set their value..
49224                         (field.store && !field.store.isLocal)
49225                         ) {
49226                         // it's a combo
49227                         var sd = { };
49228                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
49229                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
49230                         field.setFromData(sd);
49231                         
49232                     } else {
49233                         field.setValue(values[id]);
49234                     }
49235                     
49236                     
49237                     if(this.trackResetOnLoad){
49238                         field.originalValue = field.getValue();
49239                     }
49240                 }
49241             }
49242         }
49243         this.resetHasChanged();
49244         
49245         
49246         Roo.each(this.childForms || [], function (f) {
49247             f.setValues(values);
49248             f.resetHasChanged();
49249         });
49250                 
49251         return this;
49252     },
49253  
49254     /**
49255      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
49256      * they are returned as an array.
49257      * @param {Boolean} asString
49258      * @return {Object}
49259      */
49260     getValues : function(asString){
49261         if (this.childForms) {
49262             // copy values from the child forms
49263             Roo.each(this.childForms, function (f) {
49264                 this.setValues(f.getValues());
49265             }, this);
49266         }
49267         
49268         // use formdata
49269         if (typeof(FormData) != 'undefined' && asString !== true) {
49270             // this relies on a 'recent' version of chrome apparently...
49271             try {
49272                 var fd = (new FormData(this.el.dom)).entries();
49273                 var ret = {};
49274                 var ent = fd.next();
49275                 while (!ent.done) {
49276                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
49277                     ent = fd.next();
49278                 };
49279                 return ret;
49280             } catch(e) {
49281                 
49282             }
49283             
49284         }
49285         
49286         
49287         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
49288         if(asString === true){
49289             return fs;
49290         }
49291         return Roo.urlDecode(fs);
49292     },
49293     
49294     /**
49295      * Returns the fields in this form as an object with key/value pairs. 
49296      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
49297      * @return {Object}
49298      */
49299     getFieldValues : function(with_hidden)
49300     {
49301         if (this.childForms) {
49302             // copy values from the child forms
49303             // should this call getFieldValues - probably not as we do not currently copy
49304             // hidden fields when we generate..
49305             Roo.each(this.childForms, function (f) {
49306                 this.setValues(f.getValues());
49307             }, this);
49308         }
49309         
49310         var ret = {};
49311         this.items.each(function(f){
49312             if (!f.getName()) {
49313                 return;
49314             }
49315             var v = f.getValue();
49316             if (f.inputType =='radio') {
49317                 if (typeof(ret[f.getName()]) == 'undefined') {
49318                     ret[f.getName()] = ''; // empty..
49319                 }
49320                 
49321                 if (!f.el.dom.checked) {
49322                     return;
49323                     
49324                 }
49325                 v = f.el.dom.value;
49326                 
49327             }
49328             
49329             // not sure if this supported any more..
49330             if ((typeof(v) == 'object') && f.getRawValue) {
49331                 v = f.getRawValue() ; // dates..
49332             }
49333             // combo boxes where name != hiddenName...
49334             if (f.name != f.getName()) {
49335                 ret[f.name] = f.getRawValue();
49336             }
49337             ret[f.getName()] = v;
49338         });
49339         
49340         return ret;
49341     },
49342
49343     /**
49344      * Clears all invalid messages in this form.
49345      * @return {BasicForm} this
49346      */
49347     clearInvalid : function(){
49348         this.items.each(function(f){
49349            f.clearInvalid();
49350         });
49351         
49352         Roo.each(this.childForms || [], function (f) {
49353             f.clearInvalid();
49354         });
49355         
49356         
49357         return this;
49358     },
49359
49360     /**
49361      * Resets this form.
49362      * @return {BasicForm} this
49363      */
49364     reset : function(){
49365         this.items.each(function(f){
49366             f.reset();
49367         });
49368         
49369         Roo.each(this.childForms || [], function (f) {
49370             f.reset();
49371         });
49372         this.resetHasChanged();
49373         
49374         return this;
49375     },
49376
49377     /**
49378      * Add Roo.form components to this form.
49379      * @param {Field} field1
49380      * @param {Field} field2 (optional)
49381      * @param {Field} etc (optional)
49382      * @return {BasicForm} this
49383      */
49384     add : function(){
49385         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49386         return this;
49387     },
49388
49389
49390     /**
49391      * Removes a field from the items collection (does NOT remove its markup).
49392      * @param {Field} field
49393      * @return {BasicForm} this
49394      */
49395     remove : function(field){
49396         this.items.remove(field);
49397         return this;
49398     },
49399
49400     /**
49401      * Looks at the fields in this form, checks them for an id attribute,
49402      * and calls applyTo on the existing dom element with that id.
49403      * @return {BasicForm} this
49404      */
49405     render : function(){
49406         this.items.each(function(f){
49407             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49408                 f.applyTo(f.id);
49409             }
49410         });
49411         return this;
49412     },
49413
49414     /**
49415      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49416      * @param {Object} values
49417      * @return {BasicForm} this
49418      */
49419     applyToFields : function(o){
49420         this.items.each(function(f){
49421            Roo.apply(f, o);
49422         });
49423         return this;
49424     },
49425
49426     /**
49427      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49428      * @param {Object} values
49429      * @return {BasicForm} this
49430      */
49431     applyIfToFields : function(o){
49432         this.items.each(function(f){
49433            Roo.applyIf(f, o);
49434         });
49435         return this;
49436     }
49437 });
49438
49439 // back compat
49440 Roo.BasicForm = Roo.form.BasicForm;
49441
49442 Roo.apply(Roo.form.BasicForm, {
49443     
49444     popover : {
49445         
49446         padding : 5,
49447         
49448         isApplied : false,
49449         
49450         isMasked : false,
49451         
49452         form : false,
49453         
49454         target : false,
49455         
49456         intervalID : false,
49457         
49458         maskEl : false,
49459         
49460         apply : function()
49461         {
49462             if(this.isApplied){
49463                 return;
49464             }
49465             
49466             this.maskEl = {
49467                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
49468                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
49469                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
49470                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
49471             };
49472             
49473             this.maskEl.top.enableDisplayMode("block");
49474             this.maskEl.left.enableDisplayMode("block");
49475             this.maskEl.bottom.enableDisplayMode("block");
49476             this.maskEl.right.enableDisplayMode("block");
49477             
49478             Roo.get(document.body).on('click', function(){
49479                 this.unmask();
49480             }, this);
49481             
49482             Roo.get(document.body).on('touchstart', function(){
49483                 this.unmask();
49484             }, this);
49485             
49486             this.isApplied = true
49487         },
49488         
49489         mask : function(form, target)
49490         {
49491             this.form = form;
49492             
49493             this.target = target;
49494             
49495             if(!this.form.errorMask || !target.el){
49496                 return;
49497             }
49498             
49499             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
49500             
49501             var ot = this.target.el.calcOffsetsTo(scrollable);
49502             
49503             var scrollTo = ot[1] - this.form.maskOffset;
49504             
49505             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
49506             
49507             scrollable.scrollTo('top', scrollTo);
49508             
49509             var el = this.target.wrap || this.target.el;
49510             
49511             var box = el.getBox();
49512             
49513             this.maskEl.top.setStyle('position', 'absolute');
49514             this.maskEl.top.setStyle('z-index', 10000);
49515             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
49516             this.maskEl.top.setLeft(0);
49517             this.maskEl.top.setTop(0);
49518             this.maskEl.top.show();
49519             
49520             this.maskEl.left.setStyle('position', 'absolute');
49521             this.maskEl.left.setStyle('z-index', 10000);
49522             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
49523             this.maskEl.left.setLeft(0);
49524             this.maskEl.left.setTop(box.y - this.padding);
49525             this.maskEl.left.show();
49526
49527             this.maskEl.bottom.setStyle('position', 'absolute');
49528             this.maskEl.bottom.setStyle('z-index', 10000);
49529             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
49530             this.maskEl.bottom.setLeft(0);
49531             this.maskEl.bottom.setTop(box.bottom + this.padding);
49532             this.maskEl.bottom.show();
49533
49534             this.maskEl.right.setStyle('position', 'absolute');
49535             this.maskEl.right.setStyle('z-index', 10000);
49536             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
49537             this.maskEl.right.setLeft(box.right + this.padding);
49538             this.maskEl.right.setTop(box.y - this.padding);
49539             this.maskEl.right.show();
49540
49541             this.intervalID = window.setInterval(function() {
49542                 Roo.form.BasicForm.popover.unmask();
49543             }, 10000);
49544
49545             window.onwheel = function(){ return false;};
49546             
49547             (function(){ this.isMasked = true; }).defer(500, this);
49548             
49549         },
49550         
49551         unmask : function()
49552         {
49553             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
49554                 return;
49555             }
49556             
49557             this.maskEl.top.setStyle('position', 'absolute');
49558             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
49559             this.maskEl.top.hide();
49560
49561             this.maskEl.left.setStyle('position', 'absolute');
49562             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
49563             this.maskEl.left.hide();
49564
49565             this.maskEl.bottom.setStyle('position', 'absolute');
49566             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
49567             this.maskEl.bottom.hide();
49568
49569             this.maskEl.right.setStyle('position', 'absolute');
49570             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
49571             this.maskEl.right.hide();
49572             
49573             window.onwheel = function(){ return true;};
49574             
49575             if(this.intervalID){
49576                 window.clearInterval(this.intervalID);
49577                 this.intervalID = false;
49578             }
49579             
49580             this.isMasked = false;
49581             
49582         }
49583         
49584     }
49585     
49586 });/*
49587  * Based on:
49588  * Ext JS Library 1.1.1
49589  * Copyright(c) 2006-2007, Ext JS, LLC.
49590  *
49591  * Originally Released Under LGPL - original licence link has changed is not relivant.
49592  *
49593  * Fork - LGPL
49594  * <script type="text/javascript">
49595  */
49596
49597 /**
49598  * @class Roo.form.Form
49599  * @extends Roo.form.BasicForm
49600  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49601  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
49602  * @constructor
49603  * @param {Object} config Configuration options
49604  */
49605 Roo.form.Form = function(config){
49606     var xitems =  [];
49607     if (config.items) {
49608         xitems = config.items;
49609         delete config.items;
49610     }
49611    
49612     
49613     Roo.form.Form.superclass.constructor.call(this, null, config);
49614     this.url = this.url || this.action;
49615     if(!this.root){
49616         this.root = new Roo.form.Layout(Roo.applyIf({
49617             id: Roo.id()
49618         }, config));
49619     }
49620     this.active = this.root;
49621     /**
49622      * Array of all the buttons that have been added to this form via {@link addButton}
49623      * @type Array
49624      */
49625     this.buttons = [];
49626     this.allItems = [];
49627     this.addEvents({
49628         /**
49629          * @event clientvalidation
49630          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
49631          * @param {Form} this
49632          * @param {Boolean} valid true if the form has passed client-side validation
49633          */
49634         clientvalidation: true,
49635         /**
49636          * @event rendered
49637          * Fires when the form is rendered
49638          * @param {Roo.form.Form} form
49639          */
49640         rendered : true
49641     });
49642     
49643     if (this.progressUrl) {
49644             // push a hidden field onto the list of fields..
49645             this.addxtype( {
49646                     xns: Roo.form, 
49647                     xtype : 'Hidden', 
49648                     name : 'UPLOAD_IDENTIFIER' 
49649             });
49650         }
49651         
49652     
49653     Roo.each(xitems, this.addxtype, this);
49654     
49655 };
49656
49657 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
49658      /**
49659      * @cfg {Roo.Button} buttons[] buttons at bottom of form
49660      */
49661     
49662     /**
49663      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
49664      */
49665     /**
49666      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
49667      */
49668     /**
49669      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
49670      */
49671     buttonAlign:'center',
49672
49673     /**
49674      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
49675      */
49676     minButtonWidth:75,
49677
49678     /**
49679      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
49680      * This property cascades to child containers if not set.
49681      */
49682     labelAlign:'left',
49683
49684     /**
49685      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
49686      * fires a looping event with that state. This is required to bind buttons to the valid
49687      * state using the config value formBind:true on the button.
49688      */
49689     monitorValid : false,
49690
49691     /**
49692      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
49693      */
49694     monitorPoll : 200,
49695     
49696     /**
49697      * @cfg {String} progressUrl - Url to return progress data 
49698      */
49699     
49700     progressUrl : false,
49701     /**
49702      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
49703      * sending a formdata with extra parameters - eg uploaded elements.
49704      */
49705     
49706     formData : false,
49707     
49708     /**
49709      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
49710      * fields are added and the column is closed. If no fields are passed the column remains open
49711      * until end() is called.
49712      * @param {Object} config The config to pass to the column
49713      * @param {Field} field1 (optional)
49714      * @param {Field} field2 (optional)
49715      * @param {Field} etc (optional)
49716      * @return Column The column container object
49717      */
49718     column : function(c){
49719         var col = new Roo.form.Column(c);
49720         this.start(col);
49721         if(arguments.length > 1){ // duplicate code required because of Opera
49722             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49723             this.end();
49724         }
49725         return col;
49726     },
49727
49728     /**
49729      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
49730      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
49731      * until end() is called.
49732      * @param {Object} config The config to pass to the fieldset
49733      * @param {Field} field1 (optional)
49734      * @param {Field} field2 (optional)
49735      * @param {Field} etc (optional)
49736      * @return FieldSet The fieldset container object
49737      */
49738     fieldset : function(c){
49739         var fs = new Roo.form.FieldSet(c);
49740         this.start(fs);
49741         if(arguments.length > 1){ // duplicate code required because of Opera
49742             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49743             this.end();
49744         }
49745         return fs;
49746     },
49747
49748     /**
49749      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
49750      * fields are added and the container is closed. If no fields are passed the container remains open
49751      * until end() is called.
49752      * @param {Object} config The config to pass to the Layout
49753      * @param {Field} field1 (optional)
49754      * @param {Field} field2 (optional)
49755      * @param {Field} etc (optional)
49756      * @return Layout The container object
49757      */
49758     container : function(c){
49759         var l = new Roo.form.Layout(c);
49760         this.start(l);
49761         if(arguments.length > 1){ // duplicate code required because of Opera
49762             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49763             this.end();
49764         }
49765         return l;
49766     },
49767
49768     /**
49769      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
49770      * @param {Object} container A Roo.form.Layout or subclass of Layout
49771      * @return {Form} this
49772      */
49773     start : function(c){
49774         // cascade label info
49775         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
49776         this.active.stack.push(c);
49777         c.ownerCt = this.active;
49778         this.active = c;
49779         return this;
49780     },
49781
49782     /**
49783      * Closes the current open container
49784      * @return {Form} this
49785      */
49786     end : function(){
49787         if(this.active == this.root){
49788             return this;
49789         }
49790         this.active = this.active.ownerCt;
49791         return this;
49792     },
49793
49794     /**
49795      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
49796      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
49797      * as the label of the field.
49798      * @param {Field} field1
49799      * @param {Field} field2 (optional)
49800      * @param {Field} etc. (optional)
49801      * @return {Form} this
49802      */
49803     add : function(){
49804         this.active.stack.push.apply(this.active.stack, arguments);
49805         this.allItems.push.apply(this.allItems,arguments);
49806         var r = [];
49807         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
49808             if(a[i].isFormField){
49809                 r.push(a[i]);
49810             }
49811         }
49812         if(r.length > 0){
49813             Roo.form.Form.superclass.add.apply(this, r);
49814         }
49815         return this;
49816     },
49817     
49818
49819     
49820     
49821     
49822      /**
49823      * Find any element that has been added to a form, using it's ID or name
49824      * This can include framesets, columns etc. along with regular fields..
49825      * @param {String} id - id or name to find.
49826      
49827      * @return {Element} e - or false if nothing found.
49828      */
49829     findbyId : function(id)
49830     {
49831         var ret = false;
49832         if (!id) {
49833             return ret;
49834         }
49835         Roo.each(this.allItems, function(f){
49836             if (f.id == id || f.name == id ){
49837                 ret = f;
49838                 return false;
49839             }
49840         });
49841         return ret;
49842     },
49843
49844     
49845     
49846     /**
49847      * Render this form into the passed container. This should only be called once!
49848      * @param {String/HTMLElement/Element} container The element this component should be rendered into
49849      * @return {Form} this
49850      */
49851     render : function(ct)
49852     {
49853         
49854         
49855         
49856         ct = Roo.get(ct);
49857         var o = this.autoCreate || {
49858             tag: 'form',
49859             method : this.method || 'POST',
49860             id : this.id || Roo.id()
49861         };
49862         this.initEl(ct.createChild(o));
49863
49864         this.root.render(this.el);
49865         
49866        
49867              
49868         this.items.each(function(f){
49869             f.render('x-form-el-'+f.id);
49870         });
49871
49872         if(this.buttons.length > 0){
49873             // tables are required to maintain order and for correct IE layout
49874             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
49875                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
49876                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
49877             }}, null, true);
49878             var tr = tb.getElementsByTagName('tr')[0];
49879             for(var i = 0, len = this.buttons.length; i < len; i++) {
49880                 var b = this.buttons[i];
49881                 var td = document.createElement('td');
49882                 td.className = 'x-form-btn-td';
49883                 b.render(tr.appendChild(td));
49884             }
49885         }
49886         if(this.monitorValid){ // initialize after render
49887             this.startMonitoring();
49888         }
49889         this.fireEvent('rendered', this);
49890         return this;
49891     },
49892
49893     /**
49894      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
49895      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
49896      * object or a valid Roo.DomHelper element config
49897      * @param {Function} handler The function called when the button is clicked
49898      * @param {Object} scope (optional) The scope of the handler function
49899      * @return {Roo.Button}
49900      */
49901     addButton : function(config, handler, scope){
49902         var bc = {
49903             handler: handler,
49904             scope: scope,
49905             minWidth: this.minButtonWidth,
49906             hideParent:true
49907         };
49908         if(typeof config == "string"){
49909             bc.text = config;
49910         }else{
49911             Roo.apply(bc, config);
49912         }
49913         var btn = new Roo.Button(null, bc);
49914         this.buttons.push(btn);
49915         return btn;
49916     },
49917
49918      /**
49919      * Adds a series of form elements (using the xtype property as the factory method.
49920      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
49921      * @param {Object} config 
49922      */
49923     
49924     addxtype : function()
49925     {
49926         var ar = Array.prototype.slice.call(arguments, 0);
49927         var ret = false;
49928         for(var i = 0; i < ar.length; i++) {
49929             if (!ar[i]) {
49930                 continue; // skip -- if this happends something invalid got sent, we 
49931                 // should ignore it, as basically that interface element will not show up
49932                 // and that should be pretty obvious!!
49933             }
49934             
49935             if (Roo.form[ar[i].xtype]) {
49936                 ar[i].form = this;
49937                 var fe = Roo.factory(ar[i], Roo.form);
49938                 if (!ret) {
49939                     ret = fe;
49940                 }
49941                 fe.form = this;
49942                 if (fe.store) {
49943                     fe.store.form = this;
49944                 }
49945                 if (fe.isLayout) {  
49946                          
49947                     this.start(fe);
49948                     this.allItems.push(fe);
49949                     if (fe.items && fe.addxtype) {
49950                         fe.addxtype.apply(fe, fe.items);
49951                         delete fe.items;
49952                     }
49953                      this.end();
49954                     continue;
49955                 }
49956                 
49957                 
49958                  
49959                 this.add(fe);
49960               //  console.log('adding ' + ar[i].xtype);
49961             }
49962             if (ar[i].xtype == 'Button') {  
49963                 //console.log('adding button');
49964                 //console.log(ar[i]);
49965                 this.addButton(ar[i]);
49966                 this.allItems.push(fe);
49967                 continue;
49968             }
49969             
49970             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
49971                 alert('end is not supported on xtype any more, use items');
49972             //    this.end();
49973             //    //console.log('adding end');
49974             }
49975             
49976         }
49977         return ret;
49978     },
49979     
49980     /**
49981      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
49982      * option "monitorValid"
49983      */
49984     startMonitoring : function(){
49985         if(!this.bound){
49986             this.bound = true;
49987             Roo.TaskMgr.start({
49988                 run : this.bindHandler,
49989                 interval : this.monitorPoll || 200,
49990                 scope: this
49991             });
49992         }
49993     },
49994
49995     /**
49996      * Stops monitoring of the valid state of this form
49997      */
49998     stopMonitoring : function(){
49999         this.bound = false;
50000     },
50001
50002     // private
50003     bindHandler : function(){
50004         if(!this.bound){
50005             return false; // stops binding
50006         }
50007         var valid = true;
50008         this.items.each(function(f){
50009             if(!f.isValid(true)){
50010                 valid = false;
50011                 return false;
50012             }
50013         });
50014         for(var i = 0, len = this.buttons.length; i < len; i++){
50015             var btn = this.buttons[i];
50016             if(btn.formBind === true && btn.disabled === valid){
50017                 btn.setDisabled(!valid);
50018             }
50019         }
50020         this.fireEvent('clientvalidation', this, valid);
50021     }
50022     
50023     
50024     
50025     
50026     
50027     
50028     
50029     
50030 });
50031
50032
50033 // back compat
50034 Roo.Form = Roo.form.Form;
50035 /*
50036  * Based on:
50037  * Ext JS Library 1.1.1
50038  * Copyright(c) 2006-2007, Ext JS, LLC.
50039  *
50040  * Originally Released Under LGPL - original licence link has changed is not relivant.
50041  *
50042  * Fork - LGPL
50043  * <script type="text/javascript">
50044  */
50045
50046 // as we use this in bootstrap.
50047 Roo.namespace('Roo.form');
50048  /**
50049  * @class Roo.form.Action
50050  * Internal Class used to handle form actions
50051  * @constructor
50052  * @param {Roo.form.BasicForm} el The form element or its id
50053  * @param {Object} config Configuration options
50054  */
50055
50056  
50057  
50058 // define the action interface
50059 Roo.form.Action = function(form, options){
50060     this.form = form;
50061     this.options = options || {};
50062 };
50063 /**
50064  * Client Validation Failed
50065  * @const 
50066  */
50067 Roo.form.Action.CLIENT_INVALID = 'client';
50068 /**
50069  * Server Validation Failed
50070  * @const 
50071  */
50072 Roo.form.Action.SERVER_INVALID = 'server';
50073  /**
50074  * Connect to Server Failed
50075  * @const 
50076  */
50077 Roo.form.Action.CONNECT_FAILURE = 'connect';
50078 /**
50079  * Reading Data from Server Failed
50080  * @const 
50081  */
50082 Roo.form.Action.LOAD_FAILURE = 'load';
50083
50084 Roo.form.Action.prototype = {
50085     type : 'default',
50086     failureType : undefined,
50087     response : undefined,
50088     result : undefined,
50089
50090     // interface method
50091     run : function(options){
50092
50093     },
50094
50095     // interface method
50096     success : function(response){
50097
50098     },
50099
50100     // interface method
50101     handleResponse : function(response){
50102
50103     },
50104
50105     // default connection failure
50106     failure : function(response){
50107         
50108         this.response = response;
50109         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50110         this.form.afterAction(this, false);
50111     },
50112
50113     processResponse : function(response){
50114         this.response = response;
50115         if(!response.responseText){
50116             return true;
50117         }
50118         this.result = this.handleResponse(response);
50119         return this.result;
50120     },
50121
50122     // utility functions used internally
50123     getUrl : function(appendParams){
50124         var url = this.options.url || this.form.url || this.form.el.dom.action;
50125         if(appendParams){
50126             var p = this.getParams();
50127             if(p){
50128                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
50129             }
50130         }
50131         return url;
50132     },
50133
50134     getMethod : function(){
50135         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
50136     },
50137
50138     getParams : function(){
50139         var bp = this.form.baseParams;
50140         var p = this.options.params;
50141         if(p){
50142             if(typeof p == "object"){
50143                 p = Roo.urlEncode(Roo.applyIf(p, bp));
50144             }else if(typeof p == 'string' && bp){
50145                 p += '&' + Roo.urlEncode(bp);
50146             }
50147         }else if(bp){
50148             p = Roo.urlEncode(bp);
50149         }
50150         return p;
50151     },
50152
50153     createCallback : function(){
50154         return {
50155             success: this.success,
50156             failure: this.failure,
50157             scope: this,
50158             timeout: (this.form.timeout*1000),
50159             upload: this.form.fileUpload ? this.success : undefined
50160         };
50161     }
50162 };
50163
50164 Roo.form.Action.Submit = function(form, options){
50165     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
50166 };
50167
50168 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
50169     type : 'submit',
50170
50171     haveProgress : false,
50172     uploadComplete : false,
50173     
50174     // uploadProgress indicator.
50175     uploadProgress : function()
50176     {
50177         if (!this.form.progressUrl) {
50178             return;
50179         }
50180         
50181         if (!this.haveProgress) {
50182             Roo.MessageBox.progress("Uploading", "Uploading");
50183         }
50184         if (this.uploadComplete) {
50185            Roo.MessageBox.hide();
50186            return;
50187         }
50188         
50189         this.haveProgress = true;
50190    
50191         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
50192         
50193         var c = new Roo.data.Connection();
50194         c.request({
50195             url : this.form.progressUrl,
50196             params: {
50197                 id : uid
50198             },
50199             method: 'GET',
50200             success : function(req){
50201                //console.log(data);
50202                 var rdata = false;
50203                 var edata;
50204                 try  {
50205                    rdata = Roo.decode(req.responseText)
50206                 } catch (e) {
50207                     Roo.log("Invalid data from server..");
50208                     Roo.log(edata);
50209                     return;
50210                 }
50211                 if (!rdata || !rdata.success) {
50212                     Roo.log(rdata);
50213                     Roo.MessageBox.alert(Roo.encode(rdata));
50214                     return;
50215                 }
50216                 var data = rdata.data;
50217                 
50218                 if (this.uploadComplete) {
50219                    Roo.MessageBox.hide();
50220                    return;
50221                 }
50222                    
50223                 if (data){
50224                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
50225                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
50226                     );
50227                 }
50228                 this.uploadProgress.defer(2000,this);
50229             },
50230        
50231             failure: function(data) {
50232                 Roo.log('progress url failed ');
50233                 Roo.log(data);
50234             },
50235             scope : this
50236         });
50237            
50238     },
50239     
50240     
50241     run : function()
50242     {
50243         // run get Values on the form, so it syncs any secondary forms.
50244         this.form.getValues();
50245         
50246         var o = this.options;
50247         var method = this.getMethod();
50248         var isPost = method == 'POST';
50249         if(o.clientValidation === false || this.form.isValid()){
50250             
50251             if (this.form.progressUrl) {
50252                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
50253                     (new Date() * 1) + '' + Math.random());
50254                     
50255             } 
50256             
50257             
50258             Roo.Ajax.request(Roo.apply(this.createCallback(), {
50259                 form:this.form.el.dom,
50260                 url:this.getUrl(!isPost),
50261                 method: method,
50262                 params:isPost ? this.getParams() : null,
50263                 isUpload: this.form.fileUpload,
50264                 formData : this.form.formData
50265             }));
50266             
50267             this.uploadProgress();
50268
50269         }else if (o.clientValidation !== false){ // client validation failed
50270             this.failureType = Roo.form.Action.CLIENT_INVALID;
50271             this.form.afterAction(this, false);
50272         }
50273     },
50274
50275     success : function(response)
50276     {
50277         this.uploadComplete= true;
50278         if (this.haveProgress) {
50279             Roo.MessageBox.hide();
50280         }
50281         
50282         
50283         var result = this.processResponse(response);
50284         if(result === true || result.success){
50285             this.form.afterAction(this, true);
50286             return;
50287         }
50288         if(result.errors){
50289             this.form.markInvalid(result.errors);
50290             this.failureType = Roo.form.Action.SERVER_INVALID;
50291         }
50292         this.form.afterAction(this, false);
50293     },
50294     failure : function(response)
50295     {
50296         this.uploadComplete= true;
50297         if (this.haveProgress) {
50298             Roo.MessageBox.hide();
50299         }
50300         
50301         this.response = response;
50302         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50303         this.form.afterAction(this, false);
50304     },
50305     
50306     handleResponse : function(response){
50307         if(this.form.errorReader){
50308             var rs = this.form.errorReader.read(response);
50309             var errors = [];
50310             if(rs.records){
50311                 for(var i = 0, len = rs.records.length; i < len; i++) {
50312                     var r = rs.records[i];
50313                     errors[i] = r.data;
50314                 }
50315             }
50316             if(errors.length < 1){
50317                 errors = null;
50318             }
50319             return {
50320                 success : rs.success,
50321                 errors : errors
50322             };
50323         }
50324         var ret = false;
50325         try {
50326             ret = Roo.decode(response.responseText);
50327         } catch (e) {
50328             ret = {
50329                 success: false,
50330                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
50331                 errors : []
50332             };
50333         }
50334         return ret;
50335         
50336     }
50337 });
50338
50339
50340 Roo.form.Action.Load = function(form, options){
50341     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
50342     this.reader = this.form.reader;
50343 };
50344
50345 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
50346     type : 'load',
50347
50348     run : function(){
50349         
50350         Roo.Ajax.request(Roo.apply(
50351                 this.createCallback(), {
50352                     method:this.getMethod(),
50353                     url:this.getUrl(false),
50354                     params:this.getParams()
50355         }));
50356     },
50357
50358     success : function(response){
50359         
50360         var result = this.processResponse(response);
50361         if(result === true || !result.success || !result.data){
50362             this.failureType = Roo.form.Action.LOAD_FAILURE;
50363             this.form.afterAction(this, false);
50364             return;
50365         }
50366         this.form.clearInvalid();
50367         this.form.setValues(result.data);
50368         this.form.afterAction(this, true);
50369     },
50370
50371     handleResponse : function(response){
50372         if(this.form.reader){
50373             var rs = this.form.reader.read(response);
50374             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50375             return {
50376                 success : rs.success,
50377                 data : data
50378             };
50379         }
50380         return Roo.decode(response.responseText);
50381     }
50382 });
50383
50384 Roo.form.Action.ACTION_TYPES = {
50385     'load' : Roo.form.Action.Load,
50386     'submit' : Roo.form.Action.Submit
50387 };/*
50388  * Based on:
50389  * Ext JS Library 1.1.1
50390  * Copyright(c) 2006-2007, Ext JS, LLC.
50391  *
50392  * Originally Released Under LGPL - original licence link has changed is not relivant.
50393  *
50394  * Fork - LGPL
50395  * <script type="text/javascript">
50396  */
50397  
50398 /**
50399  * @class Roo.form.Layout
50400  * @extends Roo.Component
50401  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50402  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50403  * @constructor
50404  * @param {Object} config Configuration options
50405  */
50406 Roo.form.Layout = function(config){
50407     var xitems = [];
50408     if (config.items) {
50409         xitems = config.items;
50410         delete config.items;
50411     }
50412     Roo.form.Layout.superclass.constructor.call(this, config);
50413     this.stack = [];
50414     Roo.each(xitems, this.addxtype, this);
50415      
50416 };
50417
50418 Roo.extend(Roo.form.Layout, Roo.Component, {
50419     /**
50420      * @cfg {String/Object} autoCreate
50421      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50422      */
50423     /**
50424      * @cfg {String/Object/Function} style
50425      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50426      * a function which returns such a specification.
50427      */
50428     /**
50429      * @cfg {String} labelAlign
50430      * Valid values are "left," "top" and "right" (defaults to "left")
50431      */
50432     /**
50433      * @cfg {Number} labelWidth
50434      * Fixed width in pixels of all field labels (defaults to undefined)
50435      */
50436     /**
50437      * @cfg {Boolean} clear
50438      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
50439      */
50440     clear : true,
50441     /**
50442      * @cfg {String} labelSeparator
50443      * The separator to use after field labels (defaults to ':')
50444      */
50445     labelSeparator : ':',
50446     /**
50447      * @cfg {Boolean} hideLabels
50448      * True to suppress the display of field labels in this layout (defaults to false)
50449      */
50450     hideLabels : false,
50451
50452     // private
50453     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
50454     
50455     isLayout : true,
50456     
50457     // private
50458     onRender : function(ct, position){
50459         if(this.el){ // from markup
50460             this.el = Roo.get(this.el);
50461         }else {  // generate
50462             var cfg = this.getAutoCreate();
50463             this.el = ct.createChild(cfg, position);
50464         }
50465         if(this.style){
50466             this.el.applyStyles(this.style);
50467         }
50468         if(this.labelAlign){
50469             this.el.addClass('x-form-label-'+this.labelAlign);
50470         }
50471         if(this.hideLabels){
50472             this.labelStyle = "display:none";
50473             this.elementStyle = "padding-left:0;";
50474         }else{
50475             if(typeof this.labelWidth == 'number'){
50476                 this.labelStyle = "width:"+this.labelWidth+"px;";
50477                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
50478             }
50479             if(this.labelAlign == 'top'){
50480                 this.labelStyle = "width:auto;";
50481                 this.elementStyle = "padding-left:0;";
50482             }
50483         }
50484         var stack = this.stack;
50485         var slen = stack.length;
50486         if(slen > 0){
50487             if(!this.fieldTpl){
50488                 var t = new Roo.Template(
50489                     '<div class="x-form-item {5}">',
50490                         '<label for="{0}" style="{2}">{1}{4}</label>',
50491                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50492                         '</div>',
50493                     '</div><div class="x-form-clear-left"></div>'
50494                 );
50495                 t.disableFormats = true;
50496                 t.compile();
50497                 Roo.form.Layout.prototype.fieldTpl = t;
50498             }
50499             for(var i = 0; i < slen; i++) {
50500                 if(stack[i].isFormField){
50501                     this.renderField(stack[i]);
50502                 }else{
50503                     this.renderComponent(stack[i]);
50504                 }
50505             }
50506         }
50507         if(this.clear){
50508             this.el.createChild({cls:'x-form-clear'});
50509         }
50510     },
50511
50512     // private
50513     renderField : function(f){
50514         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
50515                f.id, //0
50516                f.fieldLabel, //1
50517                f.labelStyle||this.labelStyle||'', //2
50518                this.elementStyle||'', //3
50519                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
50520                f.itemCls||this.itemCls||''  //5
50521        ], true).getPrevSibling());
50522     },
50523
50524     // private
50525     renderComponent : function(c){
50526         c.render(c.isLayout ? this.el : this.el.createChild());    
50527     },
50528     /**
50529      * Adds a object form elements (using the xtype property as the factory method.)
50530      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
50531      * @param {Object} config 
50532      */
50533     addxtype : function(o)
50534     {
50535         // create the lement.
50536         o.form = this.form;
50537         var fe = Roo.factory(o, Roo.form);
50538         this.form.allItems.push(fe);
50539         this.stack.push(fe);
50540         
50541         if (fe.isFormField) {
50542             this.form.items.add(fe);
50543         }
50544          
50545         return fe;
50546     }
50547 });
50548
50549 /**
50550  * @class Roo.form.Column
50551  * @extends Roo.form.Layout
50552  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
50553  * @constructor
50554  * @param {Object} config Configuration options
50555  */
50556 Roo.form.Column = function(config){
50557     Roo.form.Column.superclass.constructor.call(this, config);
50558 };
50559
50560 Roo.extend(Roo.form.Column, Roo.form.Layout, {
50561     /**
50562      * @cfg {Number/String} width
50563      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50564      */
50565     /**
50566      * @cfg {String/Object} autoCreate
50567      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
50568      */
50569
50570     // private
50571     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
50572
50573     // private
50574     onRender : function(ct, position){
50575         Roo.form.Column.superclass.onRender.call(this, ct, position);
50576         if(this.width){
50577             this.el.setWidth(this.width);
50578         }
50579     }
50580 });
50581
50582
50583 /**
50584  * @class Roo.form.Row
50585  * @extends Roo.form.Layout
50586  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50587  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
50588  * @constructor
50589  * @param {Object} config Configuration options
50590  */
50591
50592  
50593 Roo.form.Row = function(config){
50594     Roo.form.Row.superclass.constructor.call(this, config);
50595 };
50596  
50597 Roo.extend(Roo.form.Row, Roo.form.Layout, {
50598       /**
50599      * @cfg {Number/String} width
50600      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50601      */
50602     /**
50603      * @cfg {Number/String} height
50604      * The fixed height of the column in pixels or CSS value (defaults to "auto")
50605      */
50606     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
50607     
50608     padWidth : 20,
50609     // private
50610     onRender : function(ct, position){
50611         //console.log('row render');
50612         if(!this.rowTpl){
50613             var t = new Roo.Template(
50614                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
50615                     '<label for="{0}" style="{2}">{1}{4}</label>',
50616                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50617                     '</div>',
50618                 '</div>'
50619             );
50620             t.disableFormats = true;
50621             t.compile();
50622             Roo.form.Layout.prototype.rowTpl = t;
50623         }
50624         this.fieldTpl = this.rowTpl;
50625         
50626         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
50627         var labelWidth = 100;
50628         
50629         if ((this.labelAlign != 'top')) {
50630             if (typeof this.labelWidth == 'number') {
50631                 labelWidth = this.labelWidth
50632             }
50633             this.padWidth =  20 + labelWidth;
50634             
50635         }
50636         
50637         Roo.form.Column.superclass.onRender.call(this, ct, position);
50638         if(this.width){
50639             this.el.setWidth(this.width);
50640         }
50641         if(this.height){
50642             this.el.setHeight(this.height);
50643         }
50644     },
50645     
50646     // private
50647     renderField : function(f){
50648         f.fieldEl = this.fieldTpl.append(this.el, [
50649                f.id, f.fieldLabel,
50650                f.labelStyle||this.labelStyle||'',
50651                this.elementStyle||'',
50652                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
50653                f.itemCls||this.itemCls||'',
50654                f.width ? f.width + this.padWidth : 160 + this.padWidth
50655        ],true);
50656     }
50657 });
50658  
50659
50660 /**
50661  * @class Roo.form.FieldSet
50662  * @extends Roo.form.Layout
50663  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50664  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
50665  * @constructor
50666  * @param {Object} config Configuration options
50667  */
50668 Roo.form.FieldSet = function(config){
50669     Roo.form.FieldSet.superclass.constructor.call(this, config);
50670 };
50671
50672 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
50673     /**
50674      * @cfg {String} legend
50675      * The text to display as the legend for the FieldSet (defaults to '')
50676      */
50677     /**
50678      * @cfg {String/Object} autoCreate
50679      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
50680      */
50681
50682     // private
50683     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
50684
50685     // private
50686     onRender : function(ct, position){
50687         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
50688         if(this.legend){
50689             this.setLegend(this.legend);
50690         }
50691     },
50692
50693     // private
50694     setLegend : function(text){
50695         if(this.rendered){
50696             this.el.child('legend').update(text);
50697         }
50698     }
50699 });/*
50700  * Based on:
50701  * Ext JS Library 1.1.1
50702  * Copyright(c) 2006-2007, Ext JS, LLC.
50703  *
50704  * Originally Released Under LGPL - original licence link has changed is not relivant.
50705  *
50706  * Fork - LGPL
50707  * <script type="text/javascript">
50708  */
50709 /**
50710  * @class Roo.form.VTypes
50711  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
50712  * @static
50713  */
50714 Roo.form.VTypes = function(){
50715     // closure these in so they are only created once.
50716     var alpha = /^[a-zA-Z_]+$/;
50717     var alphanum = /^[a-zA-Z0-9_]+$/;
50718     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
50719     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
50720
50721     // All these messages and functions are configurable
50722     return {
50723         /**
50724          * The function used to validate email addresses
50725          * @param {String} value The email address
50726          */
50727         'email' : function(v){
50728             return email.test(v);
50729         },
50730         /**
50731          * The error text to display when the email validation function returns false
50732          * @type String
50733          */
50734         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
50735         /**
50736          * The keystroke filter mask to be applied on email input
50737          * @type RegExp
50738          */
50739         'emailMask' : /[a-z0-9_\.\-@]/i,
50740
50741         /**
50742          * The function used to validate URLs
50743          * @param {String} value The URL
50744          */
50745         'url' : function(v){
50746             return url.test(v);
50747         },
50748         /**
50749          * The error text to display when the url validation function returns false
50750          * @type String
50751          */
50752         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
50753         
50754         /**
50755          * The function used to validate alpha values
50756          * @param {String} value The value
50757          */
50758         'alpha' : function(v){
50759             return alpha.test(v);
50760         },
50761         /**
50762          * The error text to display when the alpha validation function returns false
50763          * @type String
50764          */
50765         'alphaText' : 'This field should only contain letters and _',
50766         /**
50767          * The keystroke filter mask to be applied on alpha input
50768          * @type RegExp
50769          */
50770         'alphaMask' : /[a-z_]/i,
50771
50772         /**
50773          * The function used to validate alphanumeric values
50774          * @param {String} value The value
50775          */
50776         'alphanum' : function(v){
50777             return alphanum.test(v);
50778         },
50779         /**
50780          * The error text to display when the alphanumeric validation function returns false
50781          * @type String
50782          */
50783         'alphanumText' : 'This field should only contain letters, numbers and _',
50784         /**
50785          * The keystroke filter mask to be applied on alphanumeric input
50786          * @type RegExp
50787          */
50788         'alphanumMask' : /[a-z0-9_]/i
50789     };
50790 }();//<script type="text/javascript">
50791
50792 /**
50793  * @class Roo.form.FCKeditor
50794  * @extends Roo.form.TextArea
50795  * Wrapper around the FCKEditor http://www.fckeditor.net
50796  * @constructor
50797  * Creates a new FCKeditor
50798  * @param {Object} config Configuration options
50799  */
50800 Roo.form.FCKeditor = function(config){
50801     Roo.form.FCKeditor.superclass.constructor.call(this, config);
50802     this.addEvents({
50803          /**
50804          * @event editorinit
50805          * Fired when the editor is initialized - you can add extra handlers here..
50806          * @param {FCKeditor} this
50807          * @param {Object} the FCK object.
50808          */
50809         editorinit : true
50810     });
50811     
50812     
50813 };
50814 Roo.form.FCKeditor.editors = { };
50815 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
50816 {
50817     //defaultAutoCreate : {
50818     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
50819     //},
50820     // private
50821     /**
50822      * @cfg {Object} fck options - see fck manual for details.
50823      */
50824     fckconfig : false,
50825     
50826     /**
50827      * @cfg {Object} fck toolbar set (Basic or Default)
50828      */
50829     toolbarSet : 'Basic',
50830     /**
50831      * @cfg {Object} fck BasePath
50832      */ 
50833     basePath : '/fckeditor/',
50834     
50835     
50836     frame : false,
50837     
50838     value : '',
50839     
50840    
50841     onRender : function(ct, position)
50842     {
50843         if(!this.el){
50844             this.defaultAutoCreate = {
50845                 tag: "textarea",
50846                 style:"width:300px;height:60px;",
50847                 autocomplete: "new-password"
50848             };
50849         }
50850         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
50851         /*
50852         if(this.grow){
50853             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
50854             if(this.preventScrollbars){
50855                 this.el.setStyle("overflow", "hidden");
50856             }
50857             this.el.setHeight(this.growMin);
50858         }
50859         */
50860         //console.log('onrender' + this.getId() );
50861         Roo.form.FCKeditor.editors[this.getId()] = this;
50862          
50863
50864         this.replaceTextarea() ;
50865         
50866     },
50867     
50868     getEditor : function() {
50869         return this.fckEditor;
50870     },
50871     /**
50872      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
50873      * @param {Mixed} value The value to set
50874      */
50875     
50876     
50877     setValue : function(value)
50878     {
50879         //console.log('setValue: ' + value);
50880         
50881         if(typeof(value) == 'undefined') { // not sure why this is happending...
50882             return;
50883         }
50884         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50885         
50886         //if(!this.el || !this.getEditor()) {
50887         //    this.value = value;
50888             //this.setValue.defer(100,this,[value]);    
50889         //    return;
50890         //} 
50891         
50892         if(!this.getEditor()) {
50893             return;
50894         }
50895         
50896         this.getEditor().SetData(value);
50897         
50898         //
50899
50900     },
50901
50902     /**
50903      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
50904      * @return {Mixed} value The field value
50905      */
50906     getValue : function()
50907     {
50908         
50909         if (this.frame && this.frame.dom.style.display == 'none') {
50910             return Roo.form.FCKeditor.superclass.getValue.call(this);
50911         }
50912         
50913         if(!this.el || !this.getEditor()) {
50914            
50915            // this.getValue.defer(100,this); 
50916             return this.value;
50917         }
50918        
50919         
50920         var value=this.getEditor().GetData();
50921         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50922         return Roo.form.FCKeditor.superclass.getValue.call(this);
50923         
50924
50925     },
50926
50927     /**
50928      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
50929      * @return {Mixed} value The field value
50930      */
50931     getRawValue : function()
50932     {
50933         if (this.frame && this.frame.dom.style.display == 'none') {
50934             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50935         }
50936         
50937         if(!this.el || !this.getEditor()) {
50938             //this.getRawValue.defer(100,this); 
50939             return this.value;
50940             return;
50941         }
50942         
50943         
50944         
50945         var value=this.getEditor().GetData();
50946         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
50947         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50948          
50949     },
50950     
50951     setSize : function(w,h) {
50952         
50953         
50954         
50955         //if (this.frame && this.frame.dom.style.display == 'none') {
50956         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50957         //    return;
50958         //}
50959         //if(!this.el || !this.getEditor()) {
50960         //    this.setSize.defer(100,this, [w,h]); 
50961         //    return;
50962         //}
50963         
50964         
50965         
50966         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50967         
50968         this.frame.dom.setAttribute('width', w);
50969         this.frame.dom.setAttribute('height', h);
50970         this.frame.setSize(w,h);
50971         
50972     },
50973     
50974     toggleSourceEdit : function(value) {
50975         
50976       
50977          
50978         this.el.dom.style.display = value ? '' : 'none';
50979         this.frame.dom.style.display = value ?  'none' : '';
50980         
50981     },
50982     
50983     
50984     focus: function(tag)
50985     {
50986         if (this.frame.dom.style.display == 'none') {
50987             return Roo.form.FCKeditor.superclass.focus.call(this);
50988         }
50989         if(!this.el || !this.getEditor()) {
50990             this.focus.defer(100,this, [tag]); 
50991             return;
50992         }
50993         
50994         
50995         
50996         
50997         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
50998         this.getEditor().Focus();
50999         if (tgs.length) {
51000             if (!this.getEditor().Selection.GetSelection()) {
51001                 this.focus.defer(100,this, [tag]); 
51002                 return;
51003             }
51004             
51005             
51006             var r = this.getEditor().EditorDocument.createRange();
51007             r.setStart(tgs[0],0);
51008             r.setEnd(tgs[0],0);
51009             this.getEditor().Selection.GetSelection().removeAllRanges();
51010             this.getEditor().Selection.GetSelection().addRange(r);
51011             this.getEditor().Focus();
51012         }
51013         
51014     },
51015     
51016     
51017     
51018     replaceTextarea : function()
51019     {
51020         if ( document.getElementById( this.getId() + '___Frame' ) ) {
51021             return ;
51022         }
51023         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
51024         //{
51025             // We must check the elements firstly using the Id and then the name.
51026         var oTextarea = document.getElementById( this.getId() );
51027         
51028         var colElementsByName = document.getElementsByName( this.getId() ) ;
51029          
51030         oTextarea.style.display = 'none' ;
51031
51032         if ( oTextarea.tabIndex ) {            
51033             this.TabIndex = oTextarea.tabIndex ;
51034         }
51035         
51036         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
51037         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
51038         this.frame = Roo.get(this.getId() + '___Frame')
51039     },
51040     
51041     _getConfigHtml : function()
51042     {
51043         var sConfig = '' ;
51044
51045         for ( var o in this.fckconfig ) {
51046             sConfig += sConfig.length > 0  ? '&amp;' : '';
51047             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
51048         }
51049
51050         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
51051     },
51052     
51053     
51054     _getIFrameHtml : function()
51055     {
51056         var sFile = 'fckeditor.html' ;
51057         /* no idea what this is about..
51058         try
51059         {
51060             if ( (/fcksource=true/i).test( window.top.location.search ) )
51061                 sFile = 'fckeditor.original.html' ;
51062         }
51063         catch (e) { 
51064         */
51065
51066         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
51067         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
51068         
51069         
51070         var html = '<iframe id="' + this.getId() +
51071             '___Frame" src="' + sLink +
51072             '" width="' + this.width +
51073             '" height="' + this.height + '"' +
51074             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
51075             ' frameborder="0" scrolling="no"></iframe>' ;
51076
51077         return html ;
51078     },
51079     
51080     _insertHtmlBefore : function( html, element )
51081     {
51082         if ( element.insertAdjacentHTML )       {
51083             // IE
51084             element.insertAdjacentHTML( 'beforeBegin', html ) ;
51085         } else { // Gecko
51086             var oRange = document.createRange() ;
51087             oRange.setStartBefore( element ) ;
51088             var oFragment = oRange.createContextualFragment( html );
51089             element.parentNode.insertBefore( oFragment, element ) ;
51090         }
51091     }
51092     
51093     
51094   
51095     
51096     
51097     
51098     
51099
51100 });
51101
51102 //Roo.reg('fckeditor', Roo.form.FCKeditor);
51103
51104 function FCKeditor_OnComplete(editorInstance){
51105     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
51106     f.fckEditor = editorInstance;
51107     //console.log("loaded");
51108     f.fireEvent('editorinit', f, editorInstance);
51109
51110   
51111
51112  
51113
51114
51115
51116
51117
51118
51119
51120
51121
51122
51123
51124
51125
51126
51127
51128 //<script type="text/javascript">
51129 /**
51130  * @class Roo.form.GridField
51131  * @extends Roo.form.Field
51132  * Embed a grid (or editable grid into a form)
51133  * STATUS ALPHA
51134  * 
51135  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
51136  * it needs 
51137  * xgrid.store = Roo.data.Store
51138  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
51139  * xgrid.store.reader = Roo.data.JsonReader 
51140  * 
51141  * 
51142  * @constructor
51143  * Creates a new GridField
51144  * @param {Object} config Configuration options
51145  */
51146 Roo.form.GridField = function(config){
51147     Roo.form.GridField.superclass.constructor.call(this, config);
51148      
51149 };
51150
51151 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
51152     /**
51153      * @cfg {Number} width  - used to restrict width of grid..
51154      */
51155     width : 100,
51156     /**
51157      * @cfg {Number} height - used to restrict height of grid..
51158      */
51159     height : 50,
51160      /**
51161      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
51162          * 
51163          *}
51164      */
51165     xgrid : false, 
51166     /**
51167      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51168      * {tag: "input", type: "checkbox", autocomplete: "off"})
51169      */
51170    // defaultAutoCreate : { tag: 'div' },
51171     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
51172     /**
51173      * @cfg {String} addTitle Text to include for adding a title.
51174      */
51175     addTitle : false,
51176     //
51177     onResize : function(){
51178         Roo.form.Field.superclass.onResize.apply(this, arguments);
51179     },
51180
51181     initEvents : function(){
51182         // Roo.form.Checkbox.superclass.initEvents.call(this);
51183         // has no events...
51184        
51185     },
51186
51187
51188     getResizeEl : function(){
51189         return this.wrap;
51190     },
51191
51192     getPositionEl : function(){
51193         return this.wrap;
51194     },
51195
51196     // private
51197     onRender : function(ct, position){
51198         
51199         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
51200         var style = this.style;
51201         delete this.style;
51202         
51203         Roo.form.GridField.superclass.onRender.call(this, ct, position);
51204         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
51205         this.viewEl = this.wrap.createChild({ tag: 'div' });
51206         if (style) {
51207             this.viewEl.applyStyles(style);
51208         }
51209         if (this.width) {
51210             this.viewEl.setWidth(this.width);
51211         }
51212         if (this.height) {
51213             this.viewEl.setHeight(this.height);
51214         }
51215         //if(this.inputValue !== undefined){
51216         //this.setValue(this.value);
51217         
51218         
51219         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
51220         
51221         
51222         this.grid.render();
51223         this.grid.getDataSource().on('remove', this.refreshValue, this);
51224         this.grid.getDataSource().on('update', this.refreshValue, this);
51225         this.grid.on('afteredit', this.refreshValue, this);
51226  
51227     },
51228      
51229     
51230     /**
51231      * Sets the value of the item. 
51232      * @param {String} either an object  or a string..
51233      */
51234     setValue : function(v){
51235         //this.value = v;
51236         v = v || []; // empty set..
51237         // this does not seem smart - it really only affects memoryproxy grids..
51238         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
51239             var ds = this.grid.getDataSource();
51240             // assumes a json reader..
51241             var data = {}
51242             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
51243             ds.loadData( data);
51244         }
51245         // clear selection so it does not get stale.
51246         if (this.grid.sm) { 
51247             this.grid.sm.clearSelections();
51248         }
51249         
51250         Roo.form.GridField.superclass.setValue.call(this, v);
51251         this.refreshValue();
51252         // should load data in the grid really....
51253     },
51254     
51255     // private
51256     refreshValue: function() {
51257          var val = [];
51258         this.grid.getDataSource().each(function(r) {
51259             val.push(r.data);
51260         });
51261         this.el.dom.value = Roo.encode(val);
51262     }
51263     
51264      
51265     
51266     
51267 });/*
51268  * Based on:
51269  * Ext JS Library 1.1.1
51270  * Copyright(c) 2006-2007, Ext JS, LLC.
51271  *
51272  * Originally Released Under LGPL - original licence link has changed is not relivant.
51273  *
51274  * Fork - LGPL
51275  * <script type="text/javascript">
51276  */
51277 /**
51278  * @class Roo.form.DisplayField
51279  * @extends Roo.form.Field
51280  * A generic Field to display non-editable data.
51281  * @cfg {Boolean} closable (true|false) default false
51282  * @constructor
51283  * Creates a new Display Field item.
51284  * @param {Object} config Configuration options
51285  */
51286 Roo.form.DisplayField = function(config){
51287     Roo.form.DisplayField.superclass.constructor.call(this, config);
51288     
51289     this.addEvents({
51290         /**
51291          * @event close
51292          * Fires after the click the close btn
51293              * @param {Roo.form.DisplayField} this
51294              */
51295         close : true
51296     });
51297 };
51298
51299 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
51300     inputType:      'hidden',
51301     allowBlank:     true,
51302     readOnly:         true,
51303     
51304  
51305     /**
51306      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51307      */
51308     focusClass : undefined,
51309     /**
51310      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51311      */
51312     fieldClass: 'x-form-field',
51313     
51314      /**
51315      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
51316      */
51317     valueRenderer: undefined,
51318     
51319     width: 100,
51320     /**
51321      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51322      * {tag: "input", type: "checkbox", autocomplete: "off"})
51323      */
51324      
51325  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
51326  
51327     closable : false,
51328     
51329     onResize : function(){
51330         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
51331         
51332     },
51333
51334     initEvents : function(){
51335         // Roo.form.Checkbox.superclass.initEvents.call(this);
51336         // has no events...
51337         
51338         if(this.closable){
51339             this.closeEl.on('click', this.onClose, this);
51340         }
51341        
51342     },
51343
51344
51345     getResizeEl : function(){
51346         return this.wrap;
51347     },
51348
51349     getPositionEl : function(){
51350         return this.wrap;
51351     },
51352
51353     // private
51354     onRender : function(ct, position){
51355         
51356         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
51357         //if(this.inputValue !== undefined){
51358         this.wrap = this.el.wrap();
51359         
51360         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
51361         
51362         if(this.closable){
51363             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51364         }
51365         
51366         if (this.bodyStyle) {
51367             this.viewEl.applyStyles(this.bodyStyle);
51368         }
51369         //this.viewEl.setStyle('padding', '2px');
51370         
51371         this.setValue(this.value);
51372         
51373     },
51374 /*
51375     // private
51376     initValue : Roo.emptyFn,
51377
51378   */
51379
51380         // private
51381     onClick : function(){
51382         
51383     },
51384
51385     /**
51386      * Sets the checked state of the checkbox.
51387      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51388      */
51389     setValue : function(v){
51390         this.value = v;
51391         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51392         // this might be called before we have a dom element..
51393         if (!this.viewEl) {
51394             return;
51395         }
51396         this.viewEl.dom.innerHTML = html;
51397         Roo.form.DisplayField.superclass.setValue.call(this, v);
51398
51399     },
51400     
51401     onClose : function(e)
51402     {
51403         e.preventDefault();
51404         
51405         this.fireEvent('close', this);
51406     }
51407 });/*
51408  * 
51409  * Licence- LGPL
51410  * 
51411  */
51412
51413 /**
51414  * @class Roo.form.DayPicker
51415  * @extends Roo.form.Field
51416  * A Day picker show [M] [T] [W] ....
51417  * @constructor
51418  * Creates a new Day Picker
51419  * @param {Object} config Configuration options
51420  */
51421 Roo.form.DayPicker= function(config){
51422     Roo.form.DayPicker.superclass.constructor.call(this, config);
51423      
51424 };
51425
51426 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51427     /**
51428      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51429      */
51430     focusClass : undefined,
51431     /**
51432      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51433      */
51434     fieldClass: "x-form-field",
51435    
51436     /**
51437      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51438      * {tag: "input", type: "checkbox", autocomplete: "off"})
51439      */
51440     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
51441     
51442    
51443     actionMode : 'viewEl', 
51444     //
51445     // private
51446  
51447     inputType : 'hidden',
51448     
51449      
51450     inputElement: false, // real input element?
51451     basedOn: false, // ????
51452     
51453     isFormField: true, // not sure where this is needed!!!!
51454
51455     onResize : function(){
51456         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
51457         if(!this.boxLabel){
51458             this.el.alignTo(this.wrap, 'c-c');
51459         }
51460     },
51461
51462     initEvents : function(){
51463         Roo.form.Checkbox.superclass.initEvents.call(this);
51464         this.el.on("click", this.onClick,  this);
51465         this.el.on("change", this.onClick,  this);
51466     },
51467
51468
51469     getResizeEl : function(){
51470         return this.wrap;
51471     },
51472
51473     getPositionEl : function(){
51474         return this.wrap;
51475     },
51476
51477     
51478     // private
51479     onRender : function(ct, position){
51480         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
51481        
51482         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
51483         
51484         var r1 = '<table><tr>';
51485         var r2 = '<tr class="x-form-daypick-icons">';
51486         for (var i=0; i < 7; i++) {
51487             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
51488             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
51489         }
51490         
51491         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
51492         viewEl.select('img').on('click', this.onClick, this);
51493         this.viewEl = viewEl;   
51494         
51495         
51496         // this will not work on Chrome!!!
51497         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
51498         this.el.on('propertychange', this.setFromHidden,  this);  //ie
51499         
51500         
51501           
51502
51503     },
51504
51505     // private
51506     initValue : Roo.emptyFn,
51507
51508     /**
51509      * Returns the checked state of the checkbox.
51510      * @return {Boolean} True if checked, else false
51511      */
51512     getValue : function(){
51513         return this.el.dom.value;
51514         
51515     },
51516
51517         // private
51518     onClick : function(e){ 
51519         //this.setChecked(!this.checked);
51520         Roo.get(e.target).toggleClass('x-menu-item-checked');
51521         this.refreshValue();
51522         //if(this.el.dom.checked != this.checked){
51523         //    this.setValue(this.el.dom.checked);
51524        // }
51525     },
51526     
51527     // private
51528     refreshValue : function()
51529     {
51530         var val = '';
51531         this.viewEl.select('img',true).each(function(e,i,n)  {
51532             val += e.is(".x-menu-item-checked") ? String(n) : '';
51533         });
51534         this.setValue(val, true);
51535     },
51536
51537     /**
51538      * Sets the checked state of the checkbox.
51539      * On is always based on a string comparison between inputValue and the param.
51540      * @param {Boolean/String} value - the value to set 
51541      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
51542      */
51543     setValue : function(v,suppressEvent){
51544         if (!this.el.dom) {
51545             return;
51546         }
51547         var old = this.el.dom.value ;
51548         this.el.dom.value = v;
51549         if (suppressEvent) {
51550             return ;
51551         }
51552          
51553         // update display..
51554         this.viewEl.select('img',true).each(function(e,i,n)  {
51555             
51556             var on = e.is(".x-menu-item-checked");
51557             var newv = v.indexOf(String(n)) > -1;
51558             if (on != newv) {
51559                 e.toggleClass('x-menu-item-checked');
51560             }
51561             
51562         });
51563         
51564         
51565         this.fireEvent('change', this, v, old);
51566         
51567         
51568     },
51569    
51570     // handle setting of hidden value by some other method!!?!?
51571     setFromHidden: function()
51572     {
51573         if(!this.el){
51574             return;
51575         }
51576         //console.log("SET FROM HIDDEN");
51577         //alert('setFrom hidden');
51578         this.setValue(this.el.dom.value);
51579     },
51580     
51581     onDestroy : function()
51582     {
51583         if(this.viewEl){
51584             Roo.get(this.viewEl).remove();
51585         }
51586          
51587         Roo.form.DayPicker.superclass.onDestroy.call(this);
51588     }
51589
51590 });/*
51591  * RooJS Library 1.1.1
51592  * Copyright(c) 2008-2011  Alan Knowles
51593  *
51594  * License - LGPL
51595  */
51596  
51597
51598 /**
51599  * @class Roo.form.ComboCheck
51600  * @extends Roo.form.ComboBox
51601  * A combobox for multiple select items.
51602  *
51603  * FIXME - could do with a reset button..
51604  * 
51605  * @constructor
51606  * Create a new ComboCheck
51607  * @param {Object} config Configuration options
51608  */
51609 Roo.form.ComboCheck = function(config){
51610     Roo.form.ComboCheck.superclass.constructor.call(this, config);
51611     // should verify some data...
51612     // like
51613     // hiddenName = required..
51614     // displayField = required
51615     // valudField == required
51616     var req= [ 'hiddenName', 'displayField', 'valueField' ];
51617     var _t = this;
51618     Roo.each(req, function(e) {
51619         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
51620             throw "Roo.form.ComboCheck : missing value for: " + e;
51621         }
51622     });
51623     
51624     
51625 };
51626
51627 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
51628      
51629      
51630     editable : false,
51631      
51632     selectedClass: 'x-menu-item-checked', 
51633     
51634     // private
51635     onRender : function(ct, position){
51636         var _t = this;
51637         
51638         
51639         
51640         if(!this.tpl){
51641             var cls = 'x-combo-list';
51642
51643             
51644             this.tpl =  new Roo.Template({
51645                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
51646                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
51647                    '<span>{' + this.displayField + '}</span>' +
51648                     '</div>' 
51649                 
51650             });
51651         }
51652  
51653         
51654         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
51655         this.view.singleSelect = false;
51656         this.view.multiSelect = true;
51657         this.view.toggleSelect = true;
51658         this.pageTb.add(new Roo.Toolbar.Fill(), {
51659             
51660             text: 'Done',
51661             handler: function()
51662             {
51663                 _t.collapse();
51664             }
51665         });
51666     },
51667     
51668     onViewOver : function(e, t){
51669         // do nothing...
51670         return;
51671         
51672     },
51673     
51674     onViewClick : function(doFocus,index){
51675         return;
51676         
51677     },
51678     select: function () {
51679         //Roo.log("SELECT CALLED");
51680     },
51681      
51682     selectByValue : function(xv, scrollIntoView){
51683         var ar = this.getValueArray();
51684         var sels = [];
51685         
51686         Roo.each(ar, function(v) {
51687             if(v === undefined || v === null){
51688                 return;
51689             }
51690             var r = this.findRecord(this.valueField, v);
51691             if(r){
51692                 sels.push(this.store.indexOf(r))
51693                 
51694             }
51695         },this);
51696         this.view.select(sels);
51697         return false;
51698     },
51699     
51700     
51701     
51702     onSelect : function(record, index){
51703        // Roo.log("onselect Called");
51704        // this is only called by the clear button now..
51705         this.view.clearSelections();
51706         this.setValue('[]');
51707         if (this.value != this.valueBefore) {
51708             this.fireEvent('change', this, this.value, this.valueBefore);
51709             this.valueBefore = this.value;
51710         }
51711     },
51712     getValueArray : function()
51713     {
51714         var ar = [] ;
51715         
51716         try {
51717             //Roo.log(this.value);
51718             if (typeof(this.value) == 'undefined') {
51719                 return [];
51720             }
51721             var ar = Roo.decode(this.value);
51722             return  ar instanceof Array ? ar : []; //?? valid?
51723             
51724         } catch(e) {
51725             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
51726             return [];
51727         }
51728          
51729     },
51730     expand : function ()
51731     {
51732         
51733         Roo.form.ComboCheck.superclass.expand.call(this);
51734         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
51735         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
51736         
51737
51738     },
51739     
51740     collapse : function(){
51741         Roo.form.ComboCheck.superclass.collapse.call(this);
51742         var sl = this.view.getSelectedIndexes();
51743         var st = this.store;
51744         var nv = [];
51745         var tv = [];
51746         var r;
51747         Roo.each(sl, function(i) {
51748             r = st.getAt(i);
51749             nv.push(r.get(this.valueField));
51750         },this);
51751         this.setValue(Roo.encode(nv));
51752         if (this.value != this.valueBefore) {
51753
51754             this.fireEvent('change', this, this.value, this.valueBefore);
51755             this.valueBefore = this.value;
51756         }
51757         
51758     },
51759     
51760     setValue : function(v){
51761         // Roo.log(v);
51762         this.value = v;
51763         
51764         var vals = this.getValueArray();
51765         var tv = [];
51766         Roo.each(vals, function(k) {
51767             var r = this.findRecord(this.valueField, k);
51768             if(r){
51769                 tv.push(r.data[this.displayField]);
51770             }else if(this.valueNotFoundText !== undefined){
51771                 tv.push( this.valueNotFoundText );
51772             }
51773         },this);
51774        // Roo.log(tv);
51775         
51776         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
51777         this.hiddenField.value = v;
51778         this.value = v;
51779     }
51780     
51781 });/*
51782  * Based on:
51783  * Ext JS Library 1.1.1
51784  * Copyright(c) 2006-2007, Ext JS, LLC.
51785  *
51786  * Originally Released Under LGPL - original licence link has changed is not relivant.
51787  *
51788  * Fork - LGPL
51789  * <script type="text/javascript">
51790  */
51791  
51792 /**
51793  * @class Roo.form.Signature
51794  * @extends Roo.form.Field
51795  * Signature field.  
51796  * @constructor
51797  * 
51798  * @param {Object} config Configuration options
51799  */
51800
51801 Roo.form.Signature = function(config){
51802     Roo.form.Signature.superclass.constructor.call(this, config);
51803     
51804     this.addEvents({// not in used??
51805          /**
51806          * @event confirm
51807          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
51808              * @param {Roo.form.Signature} combo This combo box
51809              */
51810         'confirm' : true,
51811         /**
51812          * @event reset
51813          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
51814              * @param {Roo.form.ComboBox} combo This combo box
51815              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
51816              */
51817         'reset' : true
51818     });
51819 };
51820
51821 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
51822     /**
51823      * @cfg {Object} labels Label to use when rendering a form.
51824      * defaults to 
51825      * labels : { 
51826      *      clear : "Clear",
51827      *      confirm : "Confirm"
51828      *  }
51829      */
51830     labels : { 
51831         clear : "Clear",
51832         confirm : "Confirm"
51833     },
51834     /**
51835      * @cfg {Number} width The signature panel width (defaults to 300)
51836      */
51837     width: 300,
51838     /**
51839      * @cfg {Number} height The signature panel height (defaults to 100)
51840      */
51841     height : 100,
51842     /**
51843      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
51844      */
51845     allowBlank : false,
51846     
51847     //private
51848     // {Object} signPanel The signature SVG panel element (defaults to {})
51849     signPanel : {},
51850     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
51851     isMouseDown : false,
51852     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
51853     isConfirmed : false,
51854     // {String} signatureTmp SVG mapping string (defaults to empty string)
51855     signatureTmp : '',
51856     
51857     
51858     defaultAutoCreate : { // modified by initCompnoent..
51859         tag: "input",
51860         type:"hidden"
51861     },
51862
51863     // private
51864     onRender : function(ct, position){
51865         
51866         Roo.form.Signature.superclass.onRender.call(this, ct, position);
51867         
51868         this.wrap = this.el.wrap({
51869             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
51870         });
51871         
51872         this.createToolbar(this);
51873         this.signPanel = this.wrap.createChild({
51874                 tag: 'div',
51875                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
51876             }, this.el
51877         );
51878             
51879         this.svgID = Roo.id();
51880         this.svgEl = this.signPanel.createChild({
51881               xmlns : 'http://www.w3.org/2000/svg',
51882               tag : 'svg',
51883               id : this.svgID + "-svg",
51884               width: this.width,
51885               height: this.height,
51886               viewBox: '0 0 '+this.width+' '+this.height,
51887               cn : [
51888                 {
51889                     tag: "rect",
51890                     id: this.svgID + "-svg-r",
51891                     width: this.width,
51892                     height: this.height,
51893                     fill: "#ffa"
51894                 },
51895                 {
51896                     tag: "line",
51897                     id: this.svgID + "-svg-l",
51898                     x1: "0", // start
51899                     y1: (this.height*0.8), // start set the line in 80% of height
51900                     x2: this.width, // end
51901                     y2: (this.height*0.8), // end set the line in 80% of height
51902                     'stroke': "#666",
51903                     'stroke-width': "1",
51904                     'stroke-dasharray': "3",
51905                     'shape-rendering': "crispEdges",
51906                     'pointer-events': "none"
51907                 },
51908                 {
51909                     tag: "path",
51910                     id: this.svgID + "-svg-p",
51911                     'stroke': "navy",
51912                     'stroke-width': "3",
51913                     'fill': "none",
51914                     'pointer-events': 'none'
51915                 }
51916               ]
51917         });
51918         this.createSVG();
51919         this.svgBox = this.svgEl.dom.getScreenCTM();
51920     },
51921     createSVG : function(){ 
51922         var svg = this.signPanel;
51923         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
51924         var t = this;
51925
51926         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
51927         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
51928         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
51929         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
51930         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
51931         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
51932         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
51933         
51934     },
51935     isTouchEvent : function(e){
51936         return e.type.match(/^touch/);
51937     },
51938     getCoords : function (e) {
51939         var pt    = this.svgEl.dom.createSVGPoint();
51940         pt.x = e.clientX; 
51941         pt.y = e.clientY;
51942         if (this.isTouchEvent(e)) {
51943             pt.x =  e.targetTouches[0].clientX;
51944             pt.y = e.targetTouches[0].clientY;
51945         }
51946         var a = this.svgEl.dom.getScreenCTM();
51947         var b = a.inverse();
51948         var mx = pt.matrixTransform(b);
51949         return mx.x + ',' + mx.y;
51950     },
51951     //mouse event headler 
51952     down : function (e) {
51953         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
51954         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
51955         
51956         this.isMouseDown = true;
51957         
51958         e.preventDefault();
51959     },
51960     move : function (e) {
51961         if (this.isMouseDown) {
51962             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
51963             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
51964         }
51965         
51966         e.preventDefault();
51967     },
51968     up : function (e) {
51969         this.isMouseDown = false;
51970         var sp = this.signatureTmp.split(' ');
51971         
51972         if(sp.length > 1){
51973             if(!sp[sp.length-2].match(/^L/)){
51974                 sp.pop();
51975                 sp.pop();
51976                 sp.push("");
51977                 this.signatureTmp = sp.join(" ");
51978             }
51979         }
51980         if(this.getValue() != this.signatureTmp){
51981             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51982             this.isConfirmed = false;
51983         }
51984         e.preventDefault();
51985     },
51986     
51987     /**
51988      * Protected method that will not generally be called directly. It
51989      * is called when the editor creates its toolbar. Override this method if you need to
51990      * add custom toolbar buttons.
51991      * @param {HtmlEditor} editor
51992      */
51993     createToolbar : function(editor){
51994          function btn(id, toggle, handler){
51995             var xid = fid + '-'+ id ;
51996             return {
51997                 id : xid,
51998                 cmd : id,
51999                 cls : 'x-btn-icon x-edit-'+id,
52000                 enableToggle:toggle !== false,
52001                 scope: editor, // was editor...
52002                 handler:handler||editor.relayBtnCmd,
52003                 clickEvent:'mousedown',
52004                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52005                 tabIndex:-1
52006             };
52007         }
52008         
52009         
52010         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52011         this.tb = tb;
52012         this.tb.add(
52013            {
52014                 cls : ' x-signature-btn x-signature-'+id,
52015                 scope: editor, // was editor...
52016                 handler: this.reset,
52017                 clickEvent:'mousedown',
52018                 text: this.labels.clear
52019             },
52020             {
52021                  xtype : 'Fill',
52022                  xns: Roo.Toolbar
52023             }, 
52024             {
52025                 cls : '  x-signature-btn x-signature-'+id,
52026                 scope: editor, // was editor...
52027                 handler: this.confirmHandler,
52028                 clickEvent:'mousedown',
52029                 text: this.labels.confirm
52030             }
52031         );
52032     
52033     },
52034     //public
52035     /**
52036      * when user is clicked confirm then show this image.....
52037      * 
52038      * @return {String} Image Data URI
52039      */
52040     getImageDataURI : function(){
52041         var svg = this.svgEl.dom.parentNode.innerHTML;
52042         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
52043         return src; 
52044     },
52045     /**
52046      * 
52047      * @return {Boolean} this.isConfirmed
52048      */
52049     getConfirmed : function(){
52050         return this.isConfirmed;
52051     },
52052     /**
52053      * 
52054      * @return {Number} this.width
52055      */
52056     getWidth : function(){
52057         return this.width;
52058     },
52059     /**
52060      * 
52061      * @return {Number} this.height
52062      */
52063     getHeight : function(){
52064         return this.height;
52065     },
52066     // private
52067     getSignature : function(){
52068         return this.signatureTmp;
52069     },
52070     // private
52071     reset : function(){
52072         this.signatureTmp = '';
52073         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52074         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
52075         this.isConfirmed = false;
52076         Roo.form.Signature.superclass.reset.call(this);
52077     },
52078     setSignature : function(s){
52079         this.signatureTmp = s;
52080         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52081         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
52082         this.setValue(s);
52083         this.isConfirmed = false;
52084         Roo.form.Signature.superclass.reset.call(this);
52085     }, 
52086     test : function(){
52087 //        Roo.log(this.signPanel.dom.contentWindow.up())
52088     },
52089     //private
52090     setConfirmed : function(){
52091         
52092         
52093         
52094 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
52095     },
52096     // private
52097     confirmHandler : function(){
52098         if(!this.getSignature()){
52099             return;
52100         }
52101         
52102         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
52103         this.setValue(this.getSignature());
52104         this.isConfirmed = true;
52105         
52106         this.fireEvent('confirm', this);
52107     },
52108     // private
52109     // Subclasses should provide the validation implementation by overriding this
52110     validateValue : function(value){
52111         if(this.allowBlank){
52112             return true;
52113         }
52114         
52115         if(this.isConfirmed){
52116             return true;
52117         }
52118         return false;
52119     }
52120 });/*
52121  * Based on:
52122  * Ext JS Library 1.1.1
52123  * Copyright(c) 2006-2007, Ext JS, LLC.
52124  *
52125  * Originally Released Under LGPL - original licence link has changed is not relivant.
52126  *
52127  * Fork - LGPL
52128  * <script type="text/javascript">
52129  */
52130  
52131
52132 /**
52133  * @class Roo.form.ComboBox
52134  * @extends Roo.form.TriggerField
52135  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
52136  * @constructor
52137  * Create a new ComboBox.
52138  * @param {Object} config Configuration options
52139  */
52140 Roo.form.Select = function(config){
52141     Roo.form.Select.superclass.constructor.call(this, config);
52142      
52143 };
52144
52145 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
52146     /**
52147      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
52148      */
52149     /**
52150      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
52151      * rendering into an Roo.Editor, defaults to false)
52152      */
52153     /**
52154      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
52155      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
52156      */
52157     /**
52158      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
52159      */
52160     /**
52161      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
52162      * the dropdown list (defaults to undefined, with no header element)
52163      */
52164
52165      /**
52166      * @cfg {String/Roo.Template} tpl The template to use to render the output
52167      */
52168      
52169     // private
52170     defaultAutoCreate : {tag: "select"  },
52171     /**
52172      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
52173      */
52174     listWidth: undefined,
52175     /**
52176      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
52177      * mode = 'remote' or 'text' if mode = 'local')
52178      */
52179     displayField: undefined,
52180     /**
52181      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
52182      * mode = 'remote' or 'value' if mode = 'local'). 
52183      * Note: use of a valueField requires the user make a selection
52184      * in order for a value to be mapped.
52185      */
52186     valueField: undefined,
52187     
52188     
52189     /**
52190      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
52191      * field's data value (defaults to the underlying DOM element's name)
52192      */
52193     hiddenName: undefined,
52194     /**
52195      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
52196      */
52197     listClass: '',
52198     /**
52199      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
52200      */
52201     selectedClass: 'x-combo-selected',
52202     /**
52203      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
52204      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
52205      * which displays a downward arrow icon).
52206      */
52207     triggerClass : 'x-form-arrow-trigger',
52208     /**
52209      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
52210      */
52211     shadow:'sides',
52212     /**
52213      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
52214      * anchor positions (defaults to 'tl-bl')
52215      */
52216     listAlign: 'tl-bl?',
52217     /**
52218      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
52219      */
52220     maxHeight: 300,
52221     /**
52222      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
52223      * query specified by the allQuery config option (defaults to 'query')
52224      */
52225     triggerAction: 'query',
52226     /**
52227      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
52228      * (defaults to 4, does not apply if editable = false)
52229      */
52230     minChars : 4,
52231     /**
52232      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
52233      * delay (typeAheadDelay) if it matches a known value (defaults to false)
52234      */
52235     typeAhead: false,
52236     /**
52237      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
52238      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
52239      */
52240     queryDelay: 500,
52241     /**
52242      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
52243      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
52244      */
52245     pageSize: 0,
52246     /**
52247      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
52248      * when editable = true (defaults to false)
52249      */
52250     selectOnFocus:false,
52251     /**
52252      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
52253      */
52254     queryParam: 'query',
52255     /**
52256      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
52257      * when mode = 'remote' (defaults to 'Loading...')
52258      */
52259     loadingText: 'Loading...',
52260     /**
52261      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
52262      */
52263     resizable: false,
52264     /**
52265      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
52266      */
52267     handleHeight : 8,
52268     /**
52269      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
52270      * traditional select (defaults to true)
52271      */
52272     editable: true,
52273     /**
52274      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
52275      */
52276     allQuery: '',
52277     /**
52278      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
52279      */
52280     mode: 'remote',
52281     /**
52282      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
52283      * listWidth has a higher value)
52284      */
52285     minListWidth : 70,
52286     /**
52287      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
52288      * allow the user to set arbitrary text into the field (defaults to false)
52289      */
52290     forceSelection:false,
52291     /**
52292      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
52293      * if typeAhead = true (defaults to 250)
52294      */
52295     typeAheadDelay : 250,
52296     /**
52297      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
52298      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
52299      */
52300     valueNotFoundText : undefined,
52301     
52302     /**
52303      * @cfg {String} defaultValue The value displayed after loading the store.
52304      */
52305     defaultValue: '',
52306     
52307     /**
52308      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
52309      */
52310     blockFocus : false,
52311     
52312     /**
52313      * @cfg {Boolean} disableClear Disable showing of clear button.
52314      */
52315     disableClear : false,
52316     /**
52317      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
52318      */
52319     alwaysQuery : false,
52320     
52321     //private
52322     addicon : false,
52323     editicon: false,
52324     
52325     // element that contains real text value.. (when hidden is used..)
52326      
52327     // private
52328     onRender : function(ct, position){
52329         Roo.form.Field.prototype.onRender.call(this, ct, position);
52330         
52331         if(this.store){
52332             this.store.on('beforeload', this.onBeforeLoad, this);
52333             this.store.on('load', this.onLoad, this);
52334             this.store.on('loadexception', this.onLoadException, this);
52335             this.store.load({});
52336         }
52337         
52338         
52339         
52340     },
52341
52342     // private
52343     initEvents : function(){
52344         //Roo.form.ComboBox.superclass.initEvents.call(this);
52345  
52346     },
52347
52348     onDestroy : function(){
52349        
52350         if(this.store){
52351             this.store.un('beforeload', this.onBeforeLoad, this);
52352             this.store.un('load', this.onLoad, this);
52353             this.store.un('loadexception', this.onLoadException, this);
52354         }
52355         //Roo.form.ComboBox.superclass.onDestroy.call(this);
52356     },
52357
52358     // private
52359     fireKey : function(e){
52360         if(e.isNavKeyPress() && !this.list.isVisible()){
52361             this.fireEvent("specialkey", this, e);
52362         }
52363     },
52364
52365     // private
52366     onResize: function(w, h){
52367         
52368         return; 
52369     
52370         
52371     },
52372
52373     /**
52374      * Allow or prevent the user from directly editing the field text.  If false is passed,
52375      * the user will only be able to select from the items defined in the dropdown list.  This method
52376      * is the runtime equivalent of setting the 'editable' config option at config time.
52377      * @param {Boolean} value True to allow the user to directly edit the field text
52378      */
52379     setEditable : function(value){
52380          
52381     },
52382
52383     // private
52384     onBeforeLoad : function(){
52385         
52386         Roo.log("Select before load");
52387         return;
52388     
52389         this.innerList.update(this.loadingText ?
52390                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52391         //this.restrictHeight();
52392         this.selectedIndex = -1;
52393     },
52394
52395     // private
52396     onLoad : function(){
52397
52398     
52399         var dom = this.el.dom;
52400         dom.innerHTML = '';
52401          var od = dom.ownerDocument;
52402          
52403         if (this.emptyText) {
52404             var op = od.createElement('option');
52405             op.setAttribute('value', '');
52406             op.innerHTML = String.format('{0}', this.emptyText);
52407             dom.appendChild(op);
52408         }
52409         if(this.store.getCount() > 0){
52410            
52411             var vf = this.valueField;
52412             var df = this.displayField;
52413             this.store.data.each(function(r) {
52414                 // which colmsn to use... testing - cdoe / title..
52415                 var op = od.createElement('option');
52416                 op.setAttribute('value', r.data[vf]);
52417                 op.innerHTML = String.format('{0}', r.data[df]);
52418                 dom.appendChild(op);
52419             });
52420             if (typeof(this.defaultValue != 'undefined')) {
52421                 this.setValue(this.defaultValue);
52422             }
52423             
52424              
52425         }else{
52426             //this.onEmptyResults();
52427         }
52428         //this.el.focus();
52429     },
52430     // private
52431     onLoadException : function()
52432     {
52433         dom.innerHTML = '';
52434             
52435         Roo.log("Select on load exception");
52436         return;
52437     
52438         this.collapse();
52439         Roo.log(this.store.reader.jsonData);
52440         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52441             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52442         }
52443         
52444         
52445     },
52446     // private
52447     onTypeAhead : function(){
52448          
52449     },
52450
52451     // private
52452     onSelect : function(record, index){
52453         Roo.log('on select?');
52454         return;
52455         if(this.fireEvent('beforeselect', this, record, index) !== false){
52456             this.setFromData(index > -1 ? record.data : false);
52457             this.collapse();
52458             this.fireEvent('select', this, record, index);
52459         }
52460     },
52461
52462     /**
52463      * Returns the currently selected field value or empty string if no value is set.
52464      * @return {String} value The selected value
52465      */
52466     getValue : function(){
52467         var dom = this.el.dom;
52468         this.value = dom.options[dom.selectedIndex].value;
52469         return this.value;
52470         
52471     },
52472
52473     /**
52474      * Clears any text/value currently set in the field
52475      */
52476     clearValue : function(){
52477         this.value = '';
52478         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
52479         
52480     },
52481
52482     /**
52483      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
52484      * will be displayed in the field.  If the value does not match the data value of an existing item,
52485      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
52486      * Otherwise the field will be blank (although the value will still be set).
52487      * @param {String} value The value to match
52488      */
52489     setValue : function(v){
52490         var d = this.el.dom;
52491         for (var i =0; i < d.options.length;i++) {
52492             if (v == d.options[i].value) {
52493                 d.selectedIndex = i;
52494                 this.value = v;
52495                 return;
52496             }
52497         }
52498         this.clearValue();
52499     },
52500     /**
52501      * @property {Object} the last set data for the element
52502      */
52503     
52504     lastData : false,
52505     /**
52506      * Sets the value of the field based on a object which is related to the record format for the store.
52507      * @param {Object} value the value to set as. or false on reset?
52508      */
52509     setFromData : function(o){
52510         Roo.log('setfrom data?');
52511          
52512         
52513         
52514     },
52515     // private
52516     reset : function(){
52517         this.clearValue();
52518     },
52519     // private
52520     findRecord : function(prop, value){
52521         
52522         return false;
52523     
52524         var record;
52525         if(this.store.getCount() > 0){
52526             this.store.each(function(r){
52527                 if(r.data[prop] == value){
52528                     record = r;
52529                     return false;
52530                 }
52531                 return true;
52532             });
52533         }
52534         return record;
52535     },
52536     
52537     getName: function()
52538     {
52539         // returns hidden if it's set..
52540         if (!this.rendered) {return ''};
52541         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
52542         
52543     },
52544      
52545
52546     
52547
52548     // private
52549     onEmptyResults : function(){
52550         Roo.log('empty results');
52551         //this.collapse();
52552     },
52553
52554     /**
52555      * Returns true if the dropdown list is expanded, else false.
52556      */
52557     isExpanded : function(){
52558         return false;
52559     },
52560
52561     /**
52562      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
52563      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52564      * @param {String} value The data value of the item to select
52565      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52566      * selected item if it is not currently in view (defaults to true)
52567      * @return {Boolean} True if the value matched an item in the list, else false
52568      */
52569     selectByValue : function(v, scrollIntoView){
52570         Roo.log('select By Value');
52571         return false;
52572     
52573         if(v !== undefined && v !== null){
52574             var r = this.findRecord(this.valueField || this.displayField, v);
52575             if(r){
52576                 this.select(this.store.indexOf(r), scrollIntoView);
52577                 return true;
52578             }
52579         }
52580         return false;
52581     },
52582
52583     /**
52584      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
52585      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52586      * @param {Number} index The zero-based index of the list item to select
52587      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52588      * selected item if it is not currently in view (defaults to true)
52589      */
52590     select : function(index, scrollIntoView){
52591         Roo.log('select ');
52592         return  ;
52593         
52594         this.selectedIndex = index;
52595         this.view.select(index);
52596         if(scrollIntoView !== false){
52597             var el = this.view.getNode(index);
52598             if(el){
52599                 this.innerList.scrollChildIntoView(el, false);
52600             }
52601         }
52602     },
52603
52604       
52605
52606     // private
52607     validateBlur : function(){
52608         
52609         return;
52610         
52611     },
52612
52613     // private
52614     initQuery : function(){
52615         this.doQuery(this.getRawValue());
52616     },
52617
52618     // private
52619     doForce : function(){
52620         if(this.el.dom.value.length > 0){
52621             this.el.dom.value =
52622                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
52623              
52624         }
52625     },
52626
52627     /**
52628      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
52629      * query allowing the query action to be canceled if needed.
52630      * @param {String} query The SQL query to execute
52631      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
52632      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
52633      * saved in the current store (defaults to false)
52634      */
52635     doQuery : function(q, forceAll){
52636         
52637         Roo.log('doQuery?');
52638         if(q === undefined || q === null){
52639             q = '';
52640         }
52641         var qe = {
52642             query: q,
52643             forceAll: forceAll,
52644             combo: this,
52645             cancel:false
52646         };
52647         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
52648             return false;
52649         }
52650         q = qe.query;
52651         forceAll = qe.forceAll;
52652         if(forceAll === true || (q.length >= this.minChars)){
52653             if(this.lastQuery != q || this.alwaysQuery){
52654                 this.lastQuery = q;
52655                 if(this.mode == 'local'){
52656                     this.selectedIndex = -1;
52657                     if(forceAll){
52658                         this.store.clearFilter();
52659                     }else{
52660                         this.store.filter(this.displayField, q);
52661                     }
52662                     this.onLoad();
52663                 }else{
52664                     this.store.baseParams[this.queryParam] = q;
52665                     this.store.load({
52666                         params: this.getParams(q)
52667                     });
52668                     this.expand();
52669                 }
52670             }else{
52671                 this.selectedIndex = -1;
52672                 this.onLoad();   
52673             }
52674         }
52675     },
52676
52677     // private
52678     getParams : function(q){
52679         var p = {};
52680         //p[this.queryParam] = q;
52681         if(this.pageSize){
52682             p.start = 0;
52683             p.limit = this.pageSize;
52684         }
52685         return p;
52686     },
52687
52688     /**
52689      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
52690      */
52691     collapse : function(){
52692         
52693     },
52694
52695     // private
52696     collapseIf : function(e){
52697         
52698     },
52699
52700     /**
52701      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
52702      */
52703     expand : function(){
52704         
52705     } ,
52706
52707     // private
52708      
52709
52710     /** 
52711     * @cfg {Boolean} grow 
52712     * @hide 
52713     */
52714     /** 
52715     * @cfg {Number} growMin 
52716     * @hide 
52717     */
52718     /** 
52719     * @cfg {Number} growMax 
52720     * @hide 
52721     */
52722     /**
52723      * @hide
52724      * @method autoSize
52725      */
52726     
52727     setWidth : function()
52728     {
52729         
52730     },
52731     getResizeEl : function(){
52732         return this.el;
52733     }
52734 });//<script type="text/javasscript">
52735  
52736
52737 /**
52738  * @class Roo.DDView
52739  * A DnD enabled version of Roo.View.
52740  * @param {Element/String} container The Element in which to create the View.
52741  * @param {String} tpl The template string used to create the markup for each element of the View
52742  * @param {Object} config The configuration properties. These include all the config options of
52743  * {@link Roo.View} plus some specific to this class.<br>
52744  * <p>
52745  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
52746  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
52747  * <p>
52748  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
52749 .x-view-drag-insert-above {
52750         border-top:1px dotted #3366cc;
52751 }
52752 .x-view-drag-insert-below {
52753         border-bottom:1px dotted #3366cc;
52754 }
52755 </code></pre>
52756  * 
52757  */
52758  
52759 Roo.DDView = function(container, tpl, config) {
52760     Roo.DDView.superclass.constructor.apply(this, arguments);
52761     this.getEl().setStyle("outline", "0px none");
52762     this.getEl().unselectable();
52763     if (this.dragGroup) {
52764         this.setDraggable(this.dragGroup.split(","));
52765     }
52766     if (this.dropGroup) {
52767         this.setDroppable(this.dropGroup.split(","));
52768     }
52769     if (this.deletable) {
52770         this.setDeletable();
52771     }
52772     this.isDirtyFlag = false;
52773         this.addEvents({
52774                 "drop" : true
52775         });
52776 };
52777
52778 Roo.extend(Roo.DDView, Roo.View, {
52779 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
52780 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
52781 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
52782 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
52783
52784         isFormField: true,
52785
52786         reset: Roo.emptyFn,
52787         
52788         clearInvalid: Roo.form.Field.prototype.clearInvalid,
52789
52790         validate: function() {
52791                 return true;
52792         },
52793         
52794         destroy: function() {
52795                 this.purgeListeners();
52796                 this.getEl.removeAllListeners();
52797                 this.getEl().remove();
52798                 if (this.dragZone) {
52799                         if (this.dragZone.destroy) {
52800                                 this.dragZone.destroy();
52801                         }
52802                 }
52803                 if (this.dropZone) {
52804                         if (this.dropZone.destroy) {
52805                                 this.dropZone.destroy();
52806                         }
52807                 }
52808         },
52809
52810 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
52811         getName: function() {
52812                 return this.name;
52813         },
52814
52815 /**     Loads the View from a JSON string representing the Records to put into the Store. */
52816         setValue: function(v) {
52817                 if (!this.store) {
52818                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
52819                 }
52820                 var data = {};
52821                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
52822                 this.store.proxy = new Roo.data.MemoryProxy(data);
52823                 this.store.load();
52824         },
52825
52826 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
52827         getValue: function() {
52828                 var result = '(';
52829                 this.store.each(function(rec) {
52830                         result += rec.id + ',';
52831                 });
52832                 return result.substr(0, result.length - 1) + ')';
52833         },
52834         
52835         getIds: function() {
52836                 var i = 0, result = new Array(this.store.getCount());
52837                 this.store.each(function(rec) {
52838                         result[i++] = rec.id;
52839                 });
52840                 return result;
52841         },
52842         
52843         isDirty: function() {
52844                 return this.isDirtyFlag;
52845         },
52846
52847 /**
52848  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
52849  *      whole Element becomes the target, and this causes the drop gesture to append.
52850  */
52851     getTargetFromEvent : function(e) {
52852                 var target = e.getTarget();
52853                 while ((target !== null) && (target.parentNode != this.el.dom)) {
52854                 target = target.parentNode;
52855                 }
52856                 if (!target) {
52857                         target = this.el.dom.lastChild || this.el.dom;
52858                 }
52859                 return target;
52860     },
52861
52862 /**
52863  *      Create the drag data which consists of an object which has the property "ddel" as
52864  *      the drag proxy element. 
52865  */
52866     getDragData : function(e) {
52867         var target = this.findItemFromChild(e.getTarget());
52868                 if(target) {
52869                         this.handleSelection(e);
52870                         var selNodes = this.getSelectedNodes();
52871             var dragData = {
52872                 source: this,
52873                 copy: this.copy || (this.allowCopy && e.ctrlKey),
52874                 nodes: selNodes,
52875                 records: []
52876                         };
52877                         var selectedIndices = this.getSelectedIndexes();
52878                         for (var i = 0; i < selectedIndices.length; i++) {
52879                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
52880                         }
52881                         if (selNodes.length == 1) {
52882                                 dragData.ddel = target.cloneNode(true); // the div element
52883                         } else {
52884                                 var div = document.createElement('div'); // create the multi element drag "ghost"
52885                                 div.className = 'multi-proxy';
52886                                 for (var i = 0, len = selNodes.length; i < len; i++) {
52887                                         div.appendChild(selNodes[i].cloneNode(true));
52888                                 }
52889                                 dragData.ddel = div;
52890                         }
52891             //console.log(dragData)
52892             //console.log(dragData.ddel.innerHTML)
52893                         return dragData;
52894                 }
52895         //console.log('nodragData')
52896                 return false;
52897     },
52898     
52899 /**     Specify to which ddGroup items in this DDView may be dragged. */
52900     setDraggable: function(ddGroup) {
52901         if (ddGroup instanceof Array) {
52902                 Roo.each(ddGroup, this.setDraggable, this);
52903                 return;
52904         }
52905         if (this.dragZone) {
52906                 this.dragZone.addToGroup(ddGroup);
52907         } else {
52908                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
52909                                 containerScroll: true,
52910                                 ddGroup: ddGroup 
52911
52912                         });
52913 //                      Draggability implies selection. DragZone's mousedown selects the element.
52914                         if (!this.multiSelect) { this.singleSelect = true; }
52915
52916 //                      Wire the DragZone's handlers up to methods in *this*
52917                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
52918                 }
52919     },
52920
52921 /**     Specify from which ddGroup this DDView accepts drops. */
52922     setDroppable: function(ddGroup) {
52923         if (ddGroup instanceof Array) {
52924                 Roo.each(ddGroup, this.setDroppable, this);
52925                 return;
52926         }
52927         if (this.dropZone) {
52928                 this.dropZone.addToGroup(ddGroup);
52929         } else {
52930                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
52931                                 containerScroll: true,
52932                                 ddGroup: ddGroup
52933                         });
52934
52935 //                      Wire the DropZone's handlers up to methods in *this*
52936                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
52937                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
52938                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
52939                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
52940                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
52941                 }
52942     },
52943
52944 /**     Decide whether to drop above or below a View node. */
52945     getDropPoint : function(e, n, dd){
52946         if (n == this.el.dom) { return "above"; }
52947                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
52948                 var c = t + (b - t) / 2;
52949                 var y = Roo.lib.Event.getPageY(e);
52950                 if(y <= c) {
52951                         return "above";
52952                 }else{
52953                         return "below";
52954                 }
52955     },
52956
52957     onNodeEnter : function(n, dd, e, data){
52958                 return false;
52959     },
52960     
52961     onNodeOver : function(n, dd, e, data){
52962                 var pt = this.getDropPoint(e, n, dd);
52963                 // set the insert point style on the target node
52964                 var dragElClass = this.dropNotAllowed;
52965                 if (pt) {
52966                         var targetElClass;
52967                         if (pt == "above"){
52968                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
52969                                 targetElClass = "x-view-drag-insert-above";
52970                         } else {
52971                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
52972                                 targetElClass = "x-view-drag-insert-below";
52973                         }
52974                         if (this.lastInsertClass != targetElClass){
52975                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
52976                                 this.lastInsertClass = targetElClass;
52977                         }
52978                 }
52979                 return dragElClass;
52980         },
52981
52982     onNodeOut : function(n, dd, e, data){
52983                 this.removeDropIndicators(n);
52984     },
52985
52986     onNodeDrop : function(n, dd, e, data){
52987         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
52988                 return false;
52989         }
52990         var pt = this.getDropPoint(e, n, dd);
52991                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
52992                 if (pt == "below") { insertAt++; }
52993                 for (var i = 0; i < data.records.length; i++) {
52994                         var r = data.records[i];
52995                         var dup = this.store.getById(r.id);
52996                         if (dup && (dd != this.dragZone)) {
52997                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
52998                         } else {
52999                                 if (data.copy) {
53000                                         this.store.insert(insertAt++, r.copy());
53001                                 } else {
53002                                         data.source.isDirtyFlag = true;
53003                                         r.store.remove(r);
53004                                         this.store.insert(insertAt++, r);
53005                                 }
53006                                 this.isDirtyFlag = true;
53007                         }
53008                 }
53009                 this.dragZone.cachedTarget = null;
53010                 return true;
53011     },
53012
53013     removeDropIndicators : function(n){
53014                 if(n){
53015                         Roo.fly(n).removeClass([
53016                                 "x-view-drag-insert-above",
53017                                 "x-view-drag-insert-below"]);
53018                         this.lastInsertClass = "_noclass";
53019                 }
53020     },
53021
53022 /**
53023  *      Utility method. Add a delete option to the DDView's context menu.
53024  *      @param {String} imageUrl The URL of the "delete" icon image.
53025  */
53026         setDeletable: function(imageUrl) {
53027                 if (!this.singleSelect && !this.multiSelect) {
53028                         this.singleSelect = true;
53029                 }
53030                 var c = this.getContextMenu();
53031                 this.contextMenu.on("itemclick", function(item) {
53032                         switch (item.id) {
53033                                 case "delete":
53034                                         this.remove(this.getSelectedIndexes());
53035                                         break;
53036                         }
53037                 }, this);
53038                 this.contextMenu.add({
53039                         icon: imageUrl,
53040                         id: "delete",
53041                         text: 'Delete'
53042                 });
53043         },
53044         
53045 /**     Return the context menu for this DDView. */
53046         getContextMenu: function() {
53047                 if (!this.contextMenu) {
53048 //                      Create the View's context menu
53049                         this.contextMenu = new Roo.menu.Menu({
53050                                 id: this.id + "-contextmenu"
53051                         });
53052                         this.el.on("contextmenu", this.showContextMenu, this);
53053                 }
53054                 return this.contextMenu;
53055         },
53056         
53057         disableContextMenu: function() {
53058                 if (this.contextMenu) {
53059                         this.el.un("contextmenu", this.showContextMenu, this);
53060                 }
53061         },
53062
53063         showContextMenu: function(e, item) {
53064         item = this.findItemFromChild(e.getTarget());
53065                 if (item) {
53066                         e.stopEvent();
53067                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
53068                         this.contextMenu.showAt(e.getXY());
53069             }
53070     },
53071
53072 /**
53073  *      Remove {@link Roo.data.Record}s at the specified indices.
53074  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
53075  */
53076     remove: function(selectedIndices) {
53077                 selectedIndices = [].concat(selectedIndices);
53078                 for (var i = 0; i < selectedIndices.length; i++) {
53079                         var rec = this.store.getAt(selectedIndices[i]);
53080                         this.store.remove(rec);
53081                 }
53082     },
53083
53084 /**
53085  *      Double click fires the event, but also, if this is draggable, and there is only one other
53086  *      related DropZone, it transfers the selected node.
53087  */
53088     onDblClick : function(e){
53089         var item = this.findItemFromChild(e.getTarget());
53090         if(item){
53091             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
53092                 return false;
53093             }
53094             if (this.dragGroup) {
53095                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
53096                     while (targets.indexOf(this.dropZone) > -1) {
53097                             targets.remove(this.dropZone);
53098                                 }
53099                     if (targets.length == 1) {
53100                                         this.dragZone.cachedTarget = null;
53101                         var el = Roo.get(targets[0].getEl());
53102                         var box = el.getBox(true);
53103                         targets[0].onNodeDrop(el.dom, {
53104                                 target: el.dom,
53105                                 xy: [box.x, box.y + box.height - 1]
53106                         }, null, this.getDragData(e));
53107                     }
53108                 }
53109         }
53110     },
53111     
53112     handleSelection: function(e) {
53113                 this.dragZone.cachedTarget = null;
53114         var item = this.findItemFromChild(e.getTarget());
53115         if (!item) {
53116                 this.clearSelections(true);
53117                 return;
53118         }
53119                 if (item && (this.multiSelect || this.singleSelect)){
53120                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
53121                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
53122                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
53123                                 this.unselect(item);
53124                         } else {
53125                                 this.select(item, this.multiSelect && e.ctrlKey);
53126                                 this.lastSelection = item;
53127                         }
53128                 }
53129     },
53130
53131     onItemClick : function(item, index, e){
53132                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
53133                         return false;
53134                 }
53135                 return true;
53136     },
53137
53138     unselect : function(nodeInfo, suppressEvent){
53139                 var node = this.getNode(nodeInfo);
53140                 if(node && this.isSelected(node)){
53141                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
53142                                 Roo.fly(node).removeClass(this.selectedClass);
53143                                 this.selections.remove(node);
53144                                 if(!suppressEvent){
53145                                         this.fireEvent("selectionchange", this, this.selections);
53146                                 }
53147                         }
53148                 }
53149     }
53150 });
53151 /*
53152  * Based on:
53153  * Ext JS Library 1.1.1
53154  * Copyright(c) 2006-2007, Ext JS, LLC.
53155  *
53156  * Originally Released Under LGPL - original licence link has changed is not relivant.
53157  *
53158  * Fork - LGPL
53159  * <script type="text/javascript">
53160  */
53161  
53162 /**
53163  * @class Roo.LayoutManager
53164  * @extends Roo.util.Observable
53165  * Base class for layout managers.
53166  */
53167 Roo.LayoutManager = function(container, config){
53168     Roo.LayoutManager.superclass.constructor.call(this);
53169     this.el = Roo.get(container);
53170     // ie scrollbar fix
53171     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
53172         document.body.scroll = "no";
53173     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
53174         this.el.position('relative');
53175     }
53176     this.id = this.el.id;
53177     this.el.addClass("x-layout-container");
53178     /** false to disable window resize monitoring @type Boolean */
53179     this.monitorWindowResize = true;
53180     this.regions = {};
53181     this.addEvents({
53182         /**
53183          * @event layout
53184          * Fires when a layout is performed. 
53185          * @param {Roo.LayoutManager} this
53186          */
53187         "layout" : true,
53188         /**
53189          * @event regionresized
53190          * Fires when the user resizes a region. 
53191          * @param {Roo.LayoutRegion} region The resized region
53192          * @param {Number} newSize The new size (width for east/west, height for north/south)
53193          */
53194         "regionresized" : true,
53195         /**
53196          * @event regioncollapsed
53197          * Fires when a region is collapsed. 
53198          * @param {Roo.LayoutRegion} region The collapsed region
53199          */
53200         "regioncollapsed" : true,
53201         /**
53202          * @event regionexpanded
53203          * Fires when a region is expanded.  
53204          * @param {Roo.LayoutRegion} region The expanded region
53205          */
53206         "regionexpanded" : true
53207     });
53208     this.updating = false;
53209     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53210 };
53211
53212 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
53213     /**
53214      * Returns true if this layout is currently being updated
53215      * @return {Boolean}
53216      */
53217     isUpdating : function(){
53218         return this.updating; 
53219     },
53220     
53221     /**
53222      * Suspend the LayoutManager from doing auto-layouts while
53223      * making multiple add or remove calls
53224      */
53225     beginUpdate : function(){
53226         this.updating = true;    
53227     },
53228     
53229     /**
53230      * Restore auto-layouts and optionally disable the manager from performing a layout
53231      * @param {Boolean} noLayout true to disable a layout update 
53232      */
53233     endUpdate : function(noLayout){
53234         this.updating = false;
53235         if(!noLayout){
53236             this.layout();
53237         }    
53238     },
53239     
53240     layout: function(){
53241         
53242     },
53243     
53244     onRegionResized : function(region, newSize){
53245         this.fireEvent("regionresized", region, newSize);
53246         this.layout();
53247     },
53248     
53249     onRegionCollapsed : function(region){
53250         this.fireEvent("regioncollapsed", region);
53251     },
53252     
53253     onRegionExpanded : function(region){
53254         this.fireEvent("regionexpanded", region);
53255     },
53256         
53257     /**
53258      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
53259      * performs box-model adjustments.
53260      * @return {Object} The size as an object {width: (the width), height: (the height)}
53261      */
53262     getViewSize : function(){
53263         var size;
53264         if(this.el.dom != document.body){
53265             size = this.el.getSize();
53266         }else{
53267             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
53268         }
53269         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
53270         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
53271         return size;
53272     },
53273     
53274     /**
53275      * Returns the Element this layout is bound to.
53276      * @return {Roo.Element}
53277      */
53278     getEl : function(){
53279         return this.el;
53280     },
53281     
53282     /**
53283      * Returns the specified region.
53284      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
53285      * @return {Roo.LayoutRegion}
53286      */
53287     getRegion : function(target){
53288         return this.regions[target.toLowerCase()];
53289     },
53290     
53291     onWindowResize : function(){
53292         if(this.monitorWindowResize){
53293             this.layout();
53294         }
53295     }
53296 });/*
53297  * Based on:
53298  * Ext JS Library 1.1.1
53299  * Copyright(c) 2006-2007, Ext JS, LLC.
53300  *
53301  * Originally Released Under LGPL - original licence link has changed is not relivant.
53302  *
53303  * Fork - LGPL
53304  * <script type="text/javascript">
53305  */
53306 /**
53307  * @class Roo.BorderLayout
53308  * @extends Roo.LayoutManager
53309  * @children Roo.ContentPanel
53310  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
53311  * please see: <br><br>
53312  * <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>
53313  * <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>
53314  * Example:
53315  <pre><code>
53316  var layout = new Roo.BorderLayout(document.body, {
53317     north: {
53318         initialSize: 25,
53319         titlebar: false
53320     },
53321     west: {
53322         split:true,
53323         initialSize: 200,
53324         minSize: 175,
53325         maxSize: 400,
53326         titlebar: true,
53327         collapsible: true
53328     },
53329     east: {
53330         split:true,
53331         initialSize: 202,
53332         minSize: 175,
53333         maxSize: 400,
53334         titlebar: true,
53335         collapsible: true
53336     },
53337     south: {
53338         split:true,
53339         initialSize: 100,
53340         minSize: 100,
53341         maxSize: 200,
53342         titlebar: true,
53343         collapsible: true
53344     },
53345     center: {
53346         titlebar: true,
53347         autoScroll:true,
53348         resizeTabs: true,
53349         minTabWidth: 50,
53350         preferredTabWidth: 150
53351     }
53352 });
53353
53354 // shorthand
53355 var CP = Roo.ContentPanel;
53356
53357 layout.beginUpdate();
53358 layout.add("north", new CP("north", "North"));
53359 layout.add("south", new CP("south", {title: "South", closable: true}));
53360 layout.add("west", new CP("west", {title: "West"}));
53361 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
53362 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
53363 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53364 layout.getRegion("center").showPanel("center1");
53365 layout.endUpdate();
53366 </code></pre>
53367
53368 <b>The container the layout is rendered into can be either the body element or any other element.
53369 If it is not the body element, the container needs to either be an absolute positioned element,
53370 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53371 the container size if it is not the body element.</b>
53372
53373 * @constructor
53374 * Create a new BorderLayout
53375 * @param {String/HTMLElement/Element} container The container this layout is bound to
53376 * @param {Object} config Configuration options
53377  */
53378 Roo.BorderLayout = function(container, config){
53379     config = config || {};
53380     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53381     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53382     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53383         var target = this.factory.validRegions[i];
53384         if(config[target]){
53385             this.addRegion(target, config[target]);
53386         }
53387     }
53388 };
53389
53390 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53391         
53392         /**
53393          * @cfg {Roo.LayoutRegion} east
53394          */
53395         /**
53396          * @cfg {Roo.LayoutRegion} west
53397          */
53398         /**
53399          * @cfg {Roo.LayoutRegion} north
53400          */
53401         /**
53402          * @cfg {Roo.LayoutRegion} south
53403          */
53404         /**
53405          * @cfg {Roo.LayoutRegion} center
53406          */
53407     /**
53408      * Creates and adds a new region if it doesn't already exist.
53409      * @param {String} target The target region key (north, south, east, west or center).
53410      * @param {Object} config The regions config object
53411      * @return {BorderLayoutRegion} The new region
53412      */
53413     addRegion : function(target, config){
53414         if(!this.regions[target]){
53415             var r = this.factory.create(target, this, config);
53416             this.bindRegion(target, r);
53417         }
53418         return this.regions[target];
53419     },
53420
53421     // private (kinda)
53422     bindRegion : function(name, r){
53423         this.regions[name] = r;
53424         r.on("visibilitychange", this.layout, this);
53425         r.on("paneladded", this.layout, this);
53426         r.on("panelremoved", this.layout, this);
53427         r.on("invalidated", this.layout, this);
53428         r.on("resized", this.onRegionResized, this);
53429         r.on("collapsed", this.onRegionCollapsed, this);
53430         r.on("expanded", this.onRegionExpanded, this);
53431     },
53432
53433     /**
53434      * Performs a layout update.
53435      */
53436     layout : function(){
53437         if(this.updating) {
53438             return;
53439         }
53440         var size = this.getViewSize();
53441         var w = size.width;
53442         var h = size.height;
53443         var centerW = w;
53444         var centerH = h;
53445         var centerY = 0;
53446         var centerX = 0;
53447         //var x = 0, y = 0;
53448
53449         var rs = this.regions;
53450         var north = rs["north"];
53451         var south = rs["south"]; 
53452         var west = rs["west"];
53453         var east = rs["east"];
53454         var center = rs["center"];
53455         //if(this.hideOnLayout){ // not supported anymore
53456             //c.el.setStyle("display", "none");
53457         //}
53458         if(north && north.isVisible()){
53459             var b = north.getBox();
53460             var m = north.getMargins();
53461             b.width = w - (m.left+m.right);
53462             b.x = m.left;
53463             b.y = m.top;
53464             centerY = b.height + b.y + m.bottom;
53465             centerH -= centerY;
53466             north.updateBox(this.safeBox(b));
53467         }
53468         if(south && south.isVisible()){
53469             var b = south.getBox();
53470             var m = south.getMargins();
53471             b.width = w - (m.left+m.right);
53472             b.x = m.left;
53473             var totalHeight = (b.height + m.top + m.bottom);
53474             b.y = h - totalHeight + m.top;
53475             centerH -= totalHeight;
53476             south.updateBox(this.safeBox(b));
53477         }
53478         if(west && west.isVisible()){
53479             var b = west.getBox();
53480             var m = west.getMargins();
53481             b.height = centerH - (m.top+m.bottom);
53482             b.x = m.left;
53483             b.y = centerY + m.top;
53484             var totalWidth = (b.width + m.left + m.right);
53485             centerX += totalWidth;
53486             centerW -= totalWidth;
53487             west.updateBox(this.safeBox(b));
53488         }
53489         if(east && east.isVisible()){
53490             var b = east.getBox();
53491             var m = east.getMargins();
53492             b.height = centerH - (m.top+m.bottom);
53493             var totalWidth = (b.width + m.left + m.right);
53494             b.x = w - totalWidth + m.left;
53495             b.y = centerY + m.top;
53496             centerW -= totalWidth;
53497             east.updateBox(this.safeBox(b));
53498         }
53499         if(center){
53500             var m = center.getMargins();
53501             var centerBox = {
53502                 x: centerX + m.left,
53503                 y: centerY + m.top,
53504                 width: centerW - (m.left+m.right),
53505                 height: centerH - (m.top+m.bottom)
53506             };
53507             //if(this.hideOnLayout){
53508                 //center.el.setStyle("display", "block");
53509             //}
53510             center.updateBox(this.safeBox(centerBox));
53511         }
53512         this.el.repaint();
53513         this.fireEvent("layout", this);
53514     },
53515
53516     // private
53517     safeBox : function(box){
53518         box.width = Math.max(0, box.width);
53519         box.height = Math.max(0, box.height);
53520         return box;
53521     },
53522
53523     /**
53524      * Adds a ContentPanel (or subclass) to this layout.
53525      * @param {String} target The target region key (north, south, east, west or center).
53526      * @param {Roo.ContentPanel} panel The panel to add
53527      * @return {Roo.ContentPanel} The added panel
53528      */
53529     add : function(target, panel){
53530          
53531         target = target.toLowerCase();
53532         return this.regions[target].add(panel);
53533     },
53534
53535     /**
53536      * Remove a ContentPanel (or subclass) to this layout.
53537      * @param {String} target The target region key (north, south, east, west or center).
53538      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
53539      * @return {Roo.ContentPanel} The removed panel
53540      */
53541     remove : function(target, panel){
53542         target = target.toLowerCase();
53543         return this.regions[target].remove(panel);
53544     },
53545
53546     /**
53547      * Searches all regions for a panel with the specified id
53548      * @param {String} panelId
53549      * @return {Roo.ContentPanel} The panel or null if it wasn't found
53550      */
53551     findPanel : function(panelId){
53552         var rs = this.regions;
53553         for(var target in rs){
53554             if(typeof rs[target] != "function"){
53555                 var p = rs[target].getPanel(panelId);
53556                 if(p){
53557                     return p;
53558                 }
53559             }
53560         }
53561         return null;
53562     },
53563
53564     /**
53565      * Searches all regions for a panel with the specified id and activates (shows) it.
53566      * @param {String/ContentPanel} panelId The panels id or the panel itself
53567      * @return {Roo.ContentPanel} The shown panel or null
53568      */
53569     showPanel : function(panelId) {
53570       var rs = this.regions;
53571       for(var target in rs){
53572          var r = rs[target];
53573          if(typeof r != "function"){
53574             if(r.hasPanel(panelId)){
53575                return r.showPanel(panelId);
53576             }
53577          }
53578       }
53579       return null;
53580    },
53581
53582    /**
53583      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
53584      * @param {Roo.state.Provider} provider (optional) An alternate state provider
53585      */
53586     restoreState : function(provider){
53587         if(!provider){
53588             provider = Roo.state.Manager;
53589         }
53590         var sm = new Roo.LayoutStateManager();
53591         sm.init(this, provider);
53592     },
53593
53594     /**
53595      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
53596      * object should contain properties for each region to add ContentPanels to, and each property's value should be
53597      * a valid ContentPanel config object.  Example:
53598      * <pre><code>
53599 // Create the main layout
53600 var layout = new Roo.BorderLayout('main-ct', {
53601     west: {
53602         split:true,
53603         minSize: 175,
53604         titlebar: true
53605     },
53606     center: {
53607         title:'Components'
53608     }
53609 }, 'main-ct');
53610
53611 // Create and add multiple ContentPanels at once via configs
53612 layout.batchAdd({
53613    west: {
53614        id: 'source-files',
53615        autoCreate:true,
53616        title:'Ext Source Files',
53617        autoScroll:true,
53618        fitToFrame:true
53619    },
53620    center : {
53621        el: cview,
53622        autoScroll:true,
53623        fitToFrame:true,
53624        toolbar: tb,
53625        resizeEl:'cbody'
53626    }
53627 });
53628 </code></pre>
53629      * @param {Object} regions An object containing ContentPanel configs by region name
53630      */
53631     batchAdd : function(regions){
53632         this.beginUpdate();
53633         for(var rname in regions){
53634             var lr = this.regions[rname];
53635             if(lr){
53636                 this.addTypedPanels(lr, regions[rname]);
53637             }
53638         }
53639         this.endUpdate();
53640     },
53641
53642     // private
53643     addTypedPanels : function(lr, ps){
53644         if(typeof ps == 'string'){
53645             lr.add(new Roo.ContentPanel(ps));
53646         }
53647         else if(ps instanceof Array){
53648             for(var i =0, len = ps.length; i < len; i++){
53649                 this.addTypedPanels(lr, ps[i]);
53650             }
53651         }
53652         else if(!ps.events){ // raw config?
53653             var el = ps.el;
53654             delete ps.el; // prevent conflict
53655             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
53656         }
53657         else {  // panel object assumed!
53658             lr.add(ps);
53659         }
53660     },
53661     /**
53662      * Adds a xtype elements to the layout.
53663      * <pre><code>
53664
53665 layout.addxtype({
53666        xtype : 'ContentPanel',
53667        region: 'west',
53668        items: [ .... ]
53669    }
53670 );
53671
53672 layout.addxtype({
53673         xtype : 'NestedLayoutPanel',
53674         region: 'west',
53675         layout: {
53676            center: { },
53677            west: { }   
53678         },
53679         items : [ ... list of content panels or nested layout panels.. ]
53680    }
53681 );
53682 </code></pre>
53683      * @param {Object} cfg Xtype definition of item to add.
53684      */
53685     addxtype : function(cfg)
53686     {
53687         // basically accepts a pannel...
53688         // can accept a layout region..!?!?
53689         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
53690         
53691         if (!cfg.xtype.match(/Panel$/)) {
53692             return false;
53693         }
53694         var ret = false;
53695         
53696         if (typeof(cfg.region) == 'undefined') {
53697             Roo.log("Failed to add Panel, region was not set");
53698             Roo.log(cfg);
53699             return false;
53700         }
53701         var region = cfg.region;
53702         delete cfg.region;
53703         
53704           
53705         var xitems = [];
53706         if (cfg.items) {
53707             xitems = cfg.items;
53708             delete cfg.items;
53709         }
53710         var nb = false;
53711         
53712         switch(cfg.xtype) 
53713         {
53714             case 'ContentPanel':  // ContentPanel (el, cfg)
53715             case 'ScrollPanel':  // ContentPanel (el, cfg)
53716             case 'ViewPanel': 
53717                 if(cfg.autoCreate) {
53718                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53719                 } else {
53720                     var el = this.el.createChild();
53721                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
53722                 }
53723                 
53724                 this.add(region, ret);
53725                 break;
53726             
53727             
53728             case 'TreePanel': // our new panel!
53729                 cfg.el = this.el.createChild();
53730                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53731                 this.add(region, ret);
53732                 break;
53733             
53734             case 'NestedLayoutPanel': 
53735                 // create a new Layout (which is  a Border Layout...
53736                 var el = this.el.createChild();
53737                 var clayout = cfg.layout;
53738                 delete cfg.layout;
53739                 clayout.items   = clayout.items  || [];
53740                 // replace this exitems with the clayout ones..
53741                 xitems = clayout.items;
53742                  
53743                 
53744                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
53745                     cfg.background = false;
53746                 }
53747                 var layout = new Roo.BorderLayout(el, clayout);
53748                 
53749                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
53750                 //console.log('adding nested layout panel '  + cfg.toSource());
53751                 this.add(region, ret);
53752                 nb = {}; /// find first...
53753                 break;
53754                 
53755             case 'GridPanel': 
53756             
53757                 // needs grid and region
53758                 
53759                 //var el = this.getRegion(region).el.createChild();
53760                 var el = this.el.createChild();
53761                 // create the grid first...
53762                 
53763                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
53764                 delete cfg.grid;
53765                 if (region == 'center' && this.active ) {
53766                     cfg.background = false;
53767                 }
53768                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
53769                 
53770                 this.add(region, ret);
53771                 if (cfg.background) {
53772                     ret.on('activate', function(gp) {
53773                         if (!gp.grid.rendered) {
53774                             gp.grid.render();
53775                         }
53776                     });
53777                 } else {
53778                     grid.render();
53779                 }
53780                 break;
53781            
53782            
53783            
53784                 
53785                 
53786                 
53787             default:
53788                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
53789                     
53790                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53791                     this.add(region, ret);
53792                 } else {
53793                 
53794                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
53795                     return null;
53796                 }
53797                 
53798              // GridPanel (grid, cfg)
53799             
53800         }
53801         this.beginUpdate();
53802         // add children..
53803         var region = '';
53804         var abn = {};
53805         Roo.each(xitems, function(i)  {
53806             region = nb && i.region ? i.region : false;
53807             
53808             var add = ret.addxtype(i);
53809            
53810             if (region) {
53811                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
53812                 if (!i.background) {
53813                     abn[region] = nb[region] ;
53814                 }
53815             }
53816             
53817         });
53818         this.endUpdate();
53819
53820         // make the last non-background panel active..
53821         //if (nb) { Roo.log(abn); }
53822         if (nb) {
53823             
53824             for(var r in abn) {
53825                 region = this.getRegion(r);
53826                 if (region) {
53827                     // tried using nb[r], but it does not work..
53828                      
53829                     region.showPanel(abn[r]);
53830                    
53831                 }
53832             }
53833         }
53834         return ret;
53835         
53836     }
53837 });
53838
53839 /**
53840  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
53841  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
53842  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
53843  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
53844  * <pre><code>
53845 // shorthand
53846 var CP = Roo.ContentPanel;
53847
53848 var layout = Roo.BorderLayout.create({
53849     north: {
53850         initialSize: 25,
53851         titlebar: false,
53852         panels: [new CP("north", "North")]
53853     },
53854     west: {
53855         split:true,
53856         initialSize: 200,
53857         minSize: 175,
53858         maxSize: 400,
53859         titlebar: true,
53860         collapsible: true,
53861         panels: [new CP("west", {title: "West"})]
53862     },
53863     east: {
53864         split:true,
53865         initialSize: 202,
53866         minSize: 175,
53867         maxSize: 400,
53868         titlebar: true,
53869         collapsible: true,
53870         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
53871     },
53872     south: {
53873         split:true,
53874         initialSize: 100,
53875         minSize: 100,
53876         maxSize: 200,
53877         titlebar: true,
53878         collapsible: true,
53879         panels: [new CP("south", {title: "South", closable: true})]
53880     },
53881     center: {
53882         titlebar: true,
53883         autoScroll:true,
53884         resizeTabs: true,
53885         minTabWidth: 50,
53886         preferredTabWidth: 150,
53887         panels: [
53888             new CP("center1", {title: "Close Me", closable: true}),
53889             new CP("center2", {title: "Center Panel", closable: false})
53890         ]
53891     }
53892 }, document.body);
53893
53894 layout.getRegion("center").showPanel("center1");
53895 </code></pre>
53896  * @param config
53897  * @param targetEl
53898  */
53899 Roo.BorderLayout.create = function(config, targetEl){
53900     var layout = new Roo.BorderLayout(targetEl || document.body, config);
53901     layout.beginUpdate();
53902     var regions = Roo.BorderLayout.RegionFactory.validRegions;
53903     for(var j = 0, jlen = regions.length; j < jlen; j++){
53904         var lr = regions[j];
53905         if(layout.regions[lr] && config[lr].panels){
53906             var r = layout.regions[lr];
53907             var ps = config[lr].panels;
53908             layout.addTypedPanels(r, ps);
53909         }
53910     }
53911     layout.endUpdate();
53912     return layout;
53913 };
53914
53915 // private
53916 Roo.BorderLayout.RegionFactory = {
53917     // private
53918     validRegions : ["north","south","east","west","center"],
53919
53920     // private
53921     create : function(target, mgr, config){
53922         target = target.toLowerCase();
53923         if(config.lightweight || config.basic){
53924             return new Roo.BasicLayoutRegion(mgr, config, target);
53925         }
53926         switch(target){
53927             case "north":
53928                 return new Roo.NorthLayoutRegion(mgr, config);
53929             case "south":
53930                 return new Roo.SouthLayoutRegion(mgr, config);
53931             case "east":
53932                 return new Roo.EastLayoutRegion(mgr, config);
53933             case "west":
53934                 return new Roo.WestLayoutRegion(mgr, config);
53935             case "center":
53936                 return new Roo.CenterLayoutRegion(mgr, config);
53937         }
53938         throw 'Layout region "'+target+'" not supported.';
53939     }
53940 };/*
53941  * Based on:
53942  * Ext JS Library 1.1.1
53943  * Copyright(c) 2006-2007, Ext JS, LLC.
53944  *
53945  * Originally Released Under LGPL - original licence link has changed is not relivant.
53946  *
53947  * Fork - LGPL
53948  * <script type="text/javascript">
53949  */
53950  
53951 /**
53952  * @class Roo.BasicLayoutRegion
53953  * @extends Roo.util.Observable
53954  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
53955  * and does not have a titlebar, tabs or any other features. All it does is size and position 
53956  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
53957  */
53958 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
53959     this.mgr = mgr;
53960     this.position  = pos;
53961     this.events = {
53962         /**
53963          * @scope Roo.BasicLayoutRegion
53964          */
53965         
53966         /**
53967          * @event beforeremove
53968          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
53969          * @param {Roo.LayoutRegion} this
53970          * @param {Roo.ContentPanel} panel The panel
53971          * @param {Object} e The cancel event object
53972          */
53973         "beforeremove" : true,
53974         /**
53975          * @event invalidated
53976          * Fires when the layout for this region is changed.
53977          * @param {Roo.LayoutRegion} this
53978          */
53979         "invalidated" : true,
53980         /**
53981          * @event visibilitychange
53982          * Fires when this region is shown or hidden 
53983          * @param {Roo.LayoutRegion} this
53984          * @param {Boolean} visibility true or false
53985          */
53986         "visibilitychange" : true,
53987         /**
53988          * @event paneladded
53989          * Fires when a panel is added. 
53990          * @param {Roo.LayoutRegion} this
53991          * @param {Roo.ContentPanel} panel The panel
53992          */
53993         "paneladded" : true,
53994         /**
53995          * @event panelremoved
53996          * Fires when a panel is removed. 
53997          * @param {Roo.LayoutRegion} this
53998          * @param {Roo.ContentPanel} panel The panel
53999          */
54000         "panelremoved" : true,
54001         /**
54002          * @event beforecollapse
54003          * Fires when this region before collapse.
54004          * @param {Roo.LayoutRegion} this
54005          */
54006         "beforecollapse" : true,
54007         /**
54008          * @event collapsed
54009          * Fires when this region is collapsed.
54010          * @param {Roo.LayoutRegion} this
54011          */
54012         "collapsed" : true,
54013         /**
54014          * @event expanded
54015          * Fires when this region is expanded.
54016          * @param {Roo.LayoutRegion} this
54017          */
54018         "expanded" : true,
54019         /**
54020          * @event slideshow
54021          * Fires when this region is slid into view.
54022          * @param {Roo.LayoutRegion} this
54023          */
54024         "slideshow" : true,
54025         /**
54026          * @event slidehide
54027          * Fires when this region slides out of view. 
54028          * @param {Roo.LayoutRegion} this
54029          */
54030         "slidehide" : true,
54031         /**
54032          * @event panelactivated
54033          * Fires when a panel is activated. 
54034          * @param {Roo.LayoutRegion} this
54035          * @param {Roo.ContentPanel} panel The activated panel
54036          */
54037         "panelactivated" : true,
54038         /**
54039          * @event resized
54040          * Fires when the user resizes this region. 
54041          * @param {Roo.LayoutRegion} this
54042          * @param {Number} newSize The new size (width for east/west, height for north/south)
54043          */
54044         "resized" : true
54045     };
54046     /** A collection of panels in this region. @type Roo.util.MixedCollection */
54047     this.panels = new Roo.util.MixedCollection();
54048     this.panels.getKey = this.getPanelId.createDelegate(this);
54049     this.box = null;
54050     this.activePanel = null;
54051     // ensure listeners are added...
54052     
54053     if (config.listeners || config.events) {
54054         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
54055             listeners : config.listeners || {},
54056             events : config.events || {}
54057         });
54058     }
54059     
54060     if(skipConfig !== true){
54061         this.applyConfig(config);
54062     }
54063 };
54064
54065 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
54066     getPanelId : function(p){
54067         return p.getId();
54068     },
54069     
54070     applyConfig : function(config){
54071         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54072         this.config = config;
54073         
54074     },
54075     
54076     /**
54077      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
54078      * the width, for horizontal (north, south) the height.
54079      * @param {Number} newSize The new width or height
54080      */
54081     resizeTo : function(newSize){
54082         var el = this.el ? this.el :
54083                  (this.activePanel ? this.activePanel.getEl() : null);
54084         if(el){
54085             switch(this.position){
54086                 case "east":
54087                 case "west":
54088                     el.setWidth(newSize);
54089                     this.fireEvent("resized", this, newSize);
54090                 break;
54091                 case "north":
54092                 case "south":
54093                     el.setHeight(newSize);
54094                     this.fireEvent("resized", this, newSize);
54095                 break;                
54096             }
54097         }
54098     },
54099     
54100     getBox : function(){
54101         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
54102     },
54103     
54104     getMargins : function(){
54105         return this.margins;
54106     },
54107     
54108     updateBox : function(box){
54109         this.box = box;
54110         var el = this.activePanel.getEl();
54111         el.dom.style.left = box.x + "px";
54112         el.dom.style.top = box.y + "px";
54113         this.activePanel.setSize(box.width, box.height);
54114     },
54115     
54116     /**
54117      * Returns the container element for this region.
54118      * @return {Roo.Element}
54119      */
54120     getEl : function(){
54121         return this.activePanel;
54122     },
54123     
54124     /**
54125      * Returns true if this region is currently visible.
54126      * @return {Boolean}
54127      */
54128     isVisible : function(){
54129         return this.activePanel ? true : false;
54130     },
54131     
54132     setActivePanel : function(panel){
54133         panel = this.getPanel(panel);
54134         if(this.activePanel && this.activePanel != panel){
54135             this.activePanel.setActiveState(false);
54136             this.activePanel.getEl().setLeftTop(-10000,-10000);
54137         }
54138         this.activePanel = panel;
54139         panel.setActiveState(true);
54140         if(this.box){
54141             panel.setSize(this.box.width, this.box.height);
54142         }
54143         this.fireEvent("panelactivated", this, panel);
54144         this.fireEvent("invalidated");
54145     },
54146     
54147     /**
54148      * Show the specified panel.
54149      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
54150      * @return {Roo.ContentPanel} The shown panel or null
54151      */
54152     showPanel : function(panel){
54153         if(panel = this.getPanel(panel)){
54154             this.setActivePanel(panel);
54155         }
54156         return panel;
54157     },
54158     
54159     /**
54160      * Get the active panel for this region.
54161      * @return {Roo.ContentPanel} The active panel or null
54162      */
54163     getActivePanel : function(){
54164         return this.activePanel;
54165     },
54166     
54167     /**
54168      * Add the passed ContentPanel(s)
54169      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54170      * @return {Roo.ContentPanel} The panel added (if only one was added)
54171      */
54172     add : function(panel){
54173         if(arguments.length > 1){
54174             for(var i = 0, len = arguments.length; i < len; i++) {
54175                 this.add(arguments[i]);
54176             }
54177             return null;
54178         }
54179         if(this.hasPanel(panel)){
54180             this.showPanel(panel);
54181             return panel;
54182         }
54183         var el = panel.getEl();
54184         if(el.dom.parentNode != this.mgr.el.dom){
54185             this.mgr.el.dom.appendChild(el.dom);
54186         }
54187         if(panel.setRegion){
54188             panel.setRegion(this);
54189         }
54190         this.panels.add(panel);
54191         el.setStyle("position", "absolute");
54192         if(!panel.background){
54193             this.setActivePanel(panel);
54194             if(this.config.initialSize && this.panels.getCount()==1){
54195                 this.resizeTo(this.config.initialSize);
54196             }
54197         }
54198         this.fireEvent("paneladded", this, panel);
54199         return panel;
54200     },
54201     
54202     /**
54203      * Returns true if the panel is in this region.
54204      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54205      * @return {Boolean}
54206      */
54207     hasPanel : function(panel){
54208         if(typeof panel == "object"){ // must be panel obj
54209             panel = panel.getId();
54210         }
54211         return this.getPanel(panel) ? true : false;
54212     },
54213     
54214     /**
54215      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54216      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54217      * @param {Boolean} preservePanel Overrides the config preservePanel option
54218      * @return {Roo.ContentPanel} The panel that was removed
54219      */
54220     remove : function(panel, preservePanel){
54221         panel = this.getPanel(panel);
54222         if(!panel){
54223             return null;
54224         }
54225         var e = {};
54226         this.fireEvent("beforeremove", this, panel, e);
54227         if(e.cancel === true){
54228             return null;
54229         }
54230         var panelId = panel.getId();
54231         this.panels.removeKey(panelId);
54232         return panel;
54233     },
54234     
54235     /**
54236      * Returns the panel specified or null if it's not in this region.
54237      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54238      * @return {Roo.ContentPanel}
54239      */
54240     getPanel : function(id){
54241         if(typeof id == "object"){ // must be panel obj
54242             return id;
54243         }
54244         return this.panels.get(id);
54245     },
54246     
54247     /**
54248      * Returns this regions position (north/south/east/west/center).
54249      * @return {String} 
54250      */
54251     getPosition: function(){
54252         return this.position;    
54253     }
54254 });/*
54255  * Based on:
54256  * Ext JS Library 1.1.1
54257  * Copyright(c) 2006-2007, Ext JS, LLC.
54258  *
54259  * Originally Released Under LGPL - original licence link has changed is not relivant.
54260  *
54261  * Fork - LGPL
54262  * <script type="text/javascript">
54263  */
54264  
54265 /**
54266  * @class Roo.LayoutRegion
54267  * @extends Roo.BasicLayoutRegion
54268  * This class represents a region in a layout manager.
54269  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
54270  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
54271  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
54272  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
54273  * @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})
54274  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
54275  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
54276  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
54277  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
54278  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
54279  * @cfg {String}    title           The title for the region (overrides panel titles)
54280  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
54281  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
54282  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
54283  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
54284  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
54285  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
54286  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
54287  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
54288  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
54289  * @cfg {Boolean}   showPin         True to show a pin button
54290  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
54291  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
54292  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
54293  * @cfg {Number}    width           For East/West panels
54294  * @cfg {Number}    height          For North/South panels
54295  * @cfg {Boolean}   split           To show the splitter
54296  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
54297  */
54298 Roo.LayoutRegion = function(mgr, config, pos){
54299     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
54300     var dh = Roo.DomHelper;
54301     /** This region's container element 
54302     * @type Roo.Element */
54303     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
54304     /** This region's title element 
54305     * @type Roo.Element */
54306
54307     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
54308         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
54309         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
54310     ]}, true);
54311     this.titleEl.enableDisplayMode();
54312     /** This region's title text element 
54313     * @type HTMLElement */
54314     this.titleTextEl = this.titleEl.dom.firstChild;
54315     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
54316     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
54317     this.closeBtn.enableDisplayMode();
54318     this.closeBtn.on("click", this.closeClicked, this);
54319     this.closeBtn.hide();
54320
54321     this.createBody(config);
54322     this.visible = true;
54323     this.collapsed = false;
54324
54325     if(config.hideWhenEmpty){
54326         this.hide();
54327         this.on("paneladded", this.validateVisibility, this);
54328         this.on("panelremoved", this.validateVisibility, this);
54329     }
54330     this.applyConfig(config);
54331 };
54332
54333 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
54334
54335     createBody : function(){
54336         /** This region's body element 
54337         * @type Roo.Element */
54338         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
54339     },
54340
54341     applyConfig : function(c){
54342         if(c.collapsible && this.position != "center" && !this.collapsedEl){
54343             var dh = Roo.DomHelper;
54344             if(c.titlebar !== false){
54345                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
54346                 this.collapseBtn.on("click", this.collapse, this);
54347                 this.collapseBtn.enableDisplayMode();
54348
54349                 if(c.showPin === true || this.showPin){
54350                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
54351                     this.stickBtn.enableDisplayMode();
54352                     this.stickBtn.on("click", this.expand, this);
54353                     this.stickBtn.hide();
54354                 }
54355             }
54356             /** This region's collapsed element
54357             * @type Roo.Element */
54358             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
54359                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
54360             ]}, true);
54361             if(c.floatable !== false){
54362                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
54363                this.collapsedEl.on("click", this.collapseClick, this);
54364             }
54365
54366             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54367                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54368                    id: "message", unselectable: "on", style:{"float":"left"}});
54369                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54370              }
54371             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54372             this.expandBtn.on("click", this.expand, this);
54373         }
54374         if(this.collapseBtn){
54375             this.collapseBtn.setVisible(c.collapsible == true);
54376         }
54377         this.cmargins = c.cmargins || this.cmargins ||
54378                          (this.position == "west" || this.position == "east" ?
54379                              {top: 0, left: 2, right:2, bottom: 0} :
54380                              {top: 2, left: 0, right:0, bottom: 2});
54381         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54382         this.bottomTabs = c.tabPosition != "top";
54383         this.autoScroll = c.autoScroll || false;
54384         if(this.autoScroll){
54385             this.bodyEl.setStyle("overflow", "auto");
54386         }else{
54387             this.bodyEl.setStyle("overflow", "hidden");
54388         }
54389         //if(c.titlebar !== false){
54390             if((!c.titlebar && !c.title) || c.titlebar === false){
54391                 this.titleEl.hide();
54392             }else{
54393                 this.titleEl.show();
54394                 if(c.title){
54395                     this.titleTextEl.innerHTML = c.title;
54396                 }
54397             }
54398         //}
54399         this.duration = c.duration || .30;
54400         this.slideDuration = c.slideDuration || .45;
54401         this.config = c;
54402         if(c.collapsed){
54403             this.collapse(true);
54404         }
54405         if(c.hidden){
54406             this.hide();
54407         }
54408     },
54409     /**
54410      * Returns true if this region is currently visible.
54411      * @return {Boolean}
54412      */
54413     isVisible : function(){
54414         return this.visible;
54415     },
54416
54417     /**
54418      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54419      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54420      */
54421     setCollapsedTitle : function(title){
54422         title = title || "&#160;";
54423         if(this.collapsedTitleTextEl){
54424             this.collapsedTitleTextEl.innerHTML = title;
54425         }
54426     },
54427
54428     getBox : function(){
54429         var b;
54430         if(!this.collapsed){
54431             b = this.el.getBox(false, true);
54432         }else{
54433             b = this.collapsedEl.getBox(false, true);
54434         }
54435         return b;
54436     },
54437
54438     getMargins : function(){
54439         return this.collapsed ? this.cmargins : this.margins;
54440     },
54441
54442     highlight : function(){
54443         this.el.addClass("x-layout-panel-dragover");
54444     },
54445
54446     unhighlight : function(){
54447         this.el.removeClass("x-layout-panel-dragover");
54448     },
54449
54450     updateBox : function(box){
54451         this.box = box;
54452         if(!this.collapsed){
54453             this.el.dom.style.left = box.x + "px";
54454             this.el.dom.style.top = box.y + "px";
54455             this.updateBody(box.width, box.height);
54456         }else{
54457             this.collapsedEl.dom.style.left = box.x + "px";
54458             this.collapsedEl.dom.style.top = box.y + "px";
54459             this.collapsedEl.setSize(box.width, box.height);
54460         }
54461         if(this.tabs){
54462             this.tabs.autoSizeTabs();
54463         }
54464     },
54465
54466     updateBody : function(w, h){
54467         if(w !== null){
54468             this.el.setWidth(w);
54469             w -= this.el.getBorderWidth("rl");
54470             if(this.config.adjustments){
54471                 w += this.config.adjustments[0];
54472             }
54473         }
54474         if(h !== null){
54475             this.el.setHeight(h);
54476             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
54477             h -= this.el.getBorderWidth("tb");
54478             if(this.config.adjustments){
54479                 h += this.config.adjustments[1];
54480             }
54481             this.bodyEl.setHeight(h);
54482             if(this.tabs){
54483                 h = this.tabs.syncHeight(h);
54484             }
54485         }
54486         if(this.panelSize){
54487             w = w !== null ? w : this.panelSize.width;
54488             h = h !== null ? h : this.panelSize.height;
54489         }
54490         if(this.activePanel){
54491             var el = this.activePanel.getEl();
54492             w = w !== null ? w : el.getWidth();
54493             h = h !== null ? h : el.getHeight();
54494             this.panelSize = {width: w, height: h};
54495             this.activePanel.setSize(w, h);
54496         }
54497         if(Roo.isIE && this.tabs){
54498             this.tabs.el.repaint();
54499         }
54500     },
54501
54502     /**
54503      * Returns the container element for this region.
54504      * @return {Roo.Element}
54505      */
54506     getEl : function(){
54507         return this.el;
54508     },
54509
54510     /**
54511      * Hides this region.
54512      */
54513     hide : function(){
54514         if(!this.collapsed){
54515             this.el.dom.style.left = "-2000px";
54516             this.el.hide();
54517         }else{
54518             this.collapsedEl.dom.style.left = "-2000px";
54519             this.collapsedEl.hide();
54520         }
54521         this.visible = false;
54522         this.fireEvent("visibilitychange", this, false);
54523     },
54524
54525     /**
54526      * Shows this region if it was previously hidden.
54527      */
54528     show : function(){
54529         if(!this.collapsed){
54530             this.el.show();
54531         }else{
54532             this.collapsedEl.show();
54533         }
54534         this.visible = true;
54535         this.fireEvent("visibilitychange", this, true);
54536     },
54537
54538     closeClicked : function(){
54539         if(this.activePanel){
54540             this.remove(this.activePanel);
54541         }
54542     },
54543
54544     collapseClick : function(e){
54545         if(this.isSlid){
54546            e.stopPropagation();
54547            this.slideIn();
54548         }else{
54549            e.stopPropagation();
54550            this.slideOut();
54551         }
54552     },
54553
54554     /**
54555      * Collapses this region.
54556      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
54557      */
54558     collapse : function(skipAnim, skipCheck){
54559         if(this.collapsed) {
54560             return;
54561         }
54562         
54563         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
54564             
54565             this.collapsed = true;
54566             if(this.split){
54567                 this.split.el.hide();
54568             }
54569             if(this.config.animate && skipAnim !== true){
54570                 this.fireEvent("invalidated", this);
54571                 this.animateCollapse();
54572             }else{
54573                 this.el.setLocation(-20000,-20000);
54574                 this.el.hide();
54575                 this.collapsedEl.show();
54576                 this.fireEvent("collapsed", this);
54577                 this.fireEvent("invalidated", this);
54578             }
54579         }
54580         
54581     },
54582
54583     animateCollapse : function(){
54584         // overridden
54585     },
54586
54587     /**
54588      * Expands this region if it was previously collapsed.
54589      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
54590      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
54591      */
54592     expand : function(e, skipAnim){
54593         if(e) {
54594             e.stopPropagation();
54595         }
54596         if(!this.collapsed || this.el.hasActiveFx()) {
54597             return;
54598         }
54599         if(this.isSlid){
54600             this.afterSlideIn();
54601             skipAnim = true;
54602         }
54603         this.collapsed = false;
54604         if(this.config.animate && skipAnim !== true){
54605             this.animateExpand();
54606         }else{
54607             this.el.show();
54608             if(this.split){
54609                 this.split.el.show();
54610             }
54611             this.collapsedEl.setLocation(-2000,-2000);
54612             this.collapsedEl.hide();
54613             this.fireEvent("invalidated", this);
54614             this.fireEvent("expanded", this);
54615         }
54616     },
54617
54618     animateExpand : function(){
54619         // overridden
54620     },
54621
54622     initTabs : function()
54623     {
54624         this.bodyEl.setStyle("overflow", "hidden");
54625         var ts = new Roo.TabPanel(
54626                 this.bodyEl.dom,
54627                 {
54628                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
54629                     disableTooltips: this.config.disableTabTips,
54630                     toolbar : this.config.toolbar
54631                 }
54632         );
54633         if(this.config.hideTabs){
54634             ts.stripWrap.setDisplayed(false);
54635         }
54636         this.tabs = ts;
54637         ts.resizeTabs = this.config.resizeTabs === true;
54638         ts.minTabWidth = this.config.minTabWidth || 40;
54639         ts.maxTabWidth = this.config.maxTabWidth || 250;
54640         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
54641         ts.monitorResize = false;
54642         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54643         ts.bodyEl.addClass('x-layout-tabs-body');
54644         this.panels.each(this.initPanelAsTab, this);
54645     },
54646
54647     initPanelAsTab : function(panel){
54648         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
54649                     this.config.closeOnTab && panel.isClosable());
54650         if(panel.tabTip !== undefined){
54651             ti.setTooltip(panel.tabTip);
54652         }
54653         ti.on("activate", function(){
54654               this.setActivePanel(panel);
54655         }, this);
54656         if(this.config.closeOnTab){
54657             ti.on("beforeclose", function(t, e){
54658                 e.cancel = true;
54659                 this.remove(panel);
54660             }, this);
54661         }
54662         return ti;
54663     },
54664
54665     updatePanelTitle : function(panel, title){
54666         if(this.activePanel == panel){
54667             this.updateTitle(title);
54668         }
54669         if(this.tabs){
54670             var ti = this.tabs.getTab(panel.getEl().id);
54671             ti.setText(title);
54672             if(panel.tabTip !== undefined){
54673                 ti.setTooltip(panel.tabTip);
54674             }
54675         }
54676     },
54677
54678     updateTitle : function(title){
54679         if(this.titleTextEl && !this.config.title){
54680             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
54681         }
54682     },
54683
54684     setActivePanel : function(panel){
54685         panel = this.getPanel(panel);
54686         if(this.activePanel && this.activePanel != panel){
54687             this.activePanel.setActiveState(false);
54688         }
54689         this.activePanel = panel;
54690         panel.setActiveState(true);
54691         if(this.panelSize){
54692             panel.setSize(this.panelSize.width, this.panelSize.height);
54693         }
54694         if(this.closeBtn){
54695             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
54696         }
54697         this.updateTitle(panel.getTitle());
54698         if(this.tabs){
54699             this.fireEvent("invalidated", this);
54700         }
54701         this.fireEvent("panelactivated", this, panel);
54702     },
54703
54704     /**
54705      * Shows the specified panel.
54706      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
54707      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
54708      */
54709     showPanel : function(panel)
54710     {
54711         panel = this.getPanel(panel);
54712         if(panel){
54713             if(this.tabs){
54714                 var tab = this.tabs.getTab(panel.getEl().id);
54715                 if(tab.isHidden()){
54716                     this.tabs.unhideTab(tab.id);
54717                 }
54718                 tab.activate();
54719             }else{
54720                 this.setActivePanel(panel);
54721             }
54722         }
54723         return panel;
54724     },
54725
54726     /**
54727      * Get the active panel for this region.
54728      * @return {Roo.ContentPanel} The active panel or null
54729      */
54730     getActivePanel : function(){
54731         return this.activePanel;
54732     },
54733
54734     validateVisibility : function(){
54735         if(this.panels.getCount() < 1){
54736             this.updateTitle("&#160;");
54737             this.closeBtn.hide();
54738             this.hide();
54739         }else{
54740             if(!this.isVisible()){
54741                 this.show();
54742             }
54743         }
54744     },
54745
54746     /**
54747      * Adds the passed ContentPanel(s) to this region.
54748      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54749      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
54750      */
54751     add : function(panel){
54752         if(arguments.length > 1){
54753             for(var i = 0, len = arguments.length; i < len; i++) {
54754                 this.add(arguments[i]);
54755             }
54756             return null;
54757         }
54758         if(this.hasPanel(panel)){
54759             this.showPanel(panel);
54760             return panel;
54761         }
54762         panel.setRegion(this);
54763         this.panels.add(panel);
54764         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
54765             this.bodyEl.dom.appendChild(panel.getEl().dom);
54766             if(panel.background !== true){
54767                 this.setActivePanel(panel);
54768             }
54769             this.fireEvent("paneladded", this, panel);
54770             return panel;
54771         }
54772         if(!this.tabs){
54773             this.initTabs();
54774         }else{
54775             this.initPanelAsTab(panel);
54776         }
54777         if(panel.background !== true){
54778             this.tabs.activate(panel.getEl().id);
54779         }
54780         this.fireEvent("paneladded", this, panel);
54781         return panel;
54782     },
54783
54784     /**
54785      * Hides the tab for the specified panel.
54786      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54787      */
54788     hidePanel : function(panel){
54789         if(this.tabs && (panel = this.getPanel(panel))){
54790             this.tabs.hideTab(panel.getEl().id);
54791         }
54792     },
54793
54794     /**
54795      * Unhides the tab for a previously hidden panel.
54796      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54797      */
54798     unhidePanel : function(panel){
54799         if(this.tabs && (panel = this.getPanel(panel))){
54800             this.tabs.unhideTab(panel.getEl().id);
54801         }
54802     },
54803
54804     clearPanels : function(){
54805         while(this.panels.getCount() > 0){
54806              this.remove(this.panels.first());
54807         }
54808     },
54809
54810     /**
54811      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54812      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54813      * @param {Boolean} preservePanel Overrides the config preservePanel option
54814      * @return {Roo.ContentPanel} The panel that was removed
54815      */
54816     remove : function(panel, preservePanel){
54817         panel = this.getPanel(panel);
54818         if(!panel){
54819             return null;
54820         }
54821         var e = {};
54822         this.fireEvent("beforeremove", this, panel, e);
54823         if(e.cancel === true){
54824             return null;
54825         }
54826         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
54827         var panelId = panel.getId();
54828         this.panels.removeKey(panelId);
54829         if(preservePanel){
54830             document.body.appendChild(panel.getEl().dom);
54831         }
54832         if(this.tabs){
54833             this.tabs.removeTab(panel.getEl().id);
54834         }else if (!preservePanel){
54835             this.bodyEl.dom.removeChild(panel.getEl().dom);
54836         }
54837         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
54838             var p = this.panels.first();
54839             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
54840             tempEl.appendChild(p.getEl().dom);
54841             this.bodyEl.update("");
54842             this.bodyEl.dom.appendChild(p.getEl().dom);
54843             tempEl = null;
54844             this.updateTitle(p.getTitle());
54845             this.tabs = null;
54846             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54847             this.setActivePanel(p);
54848         }
54849         panel.setRegion(null);
54850         if(this.activePanel == panel){
54851             this.activePanel = null;
54852         }
54853         if(this.config.autoDestroy !== false && preservePanel !== true){
54854             try{panel.destroy();}catch(e){}
54855         }
54856         this.fireEvent("panelremoved", this, panel);
54857         return panel;
54858     },
54859
54860     /**
54861      * Returns the TabPanel component used by this region
54862      * @return {Roo.TabPanel}
54863      */
54864     getTabs : function(){
54865         return this.tabs;
54866     },
54867
54868     createTool : function(parentEl, className){
54869         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
54870             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
54871         btn.addClassOnOver("x-layout-tools-button-over");
54872         return btn;
54873     }
54874 });/*
54875  * Based on:
54876  * Ext JS Library 1.1.1
54877  * Copyright(c) 2006-2007, Ext JS, LLC.
54878  *
54879  * Originally Released Under LGPL - original licence link has changed is not relivant.
54880  *
54881  * Fork - LGPL
54882  * <script type="text/javascript">
54883  */
54884  
54885
54886
54887 /**
54888  * @class Roo.SplitLayoutRegion
54889  * @extends Roo.LayoutRegion
54890  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
54891  */
54892 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
54893     this.cursor = cursor;
54894     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
54895 };
54896
54897 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
54898     splitTip : "Drag to resize.",
54899     collapsibleSplitTip : "Drag to resize. Double click to hide.",
54900     useSplitTips : false,
54901
54902     applyConfig : function(config){
54903         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
54904         if(config.split){
54905             if(!this.split){
54906                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
54907                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
54908                 /** The SplitBar for this region 
54909                 * @type Roo.SplitBar */
54910                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
54911                 this.split.on("moved", this.onSplitMove, this);
54912                 this.split.useShim = config.useShim === true;
54913                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
54914                 if(this.useSplitTips){
54915                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
54916                 }
54917                 if(config.collapsible){
54918                     this.split.el.on("dblclick", this.collapse,  this);
54919                 }
54920             }
54921             if(typeof config.minSize != "undefined"){
54922                 this.split.minSize = config.minSize;
54923             }
54924             if(typeof config.maxSize != "undefined"){
54925                 this.split.maxSize = config.maxSize;
54926             }
54927             if(config.hideWhenEmpty || config.hidden || config.collapsed){
54928                 this.hideSplitter();
54929             }
54930         }
54931     },
54932
54933     getHMaxSize : function(){
54934          var cmax = this.config.maxSize || 10000;
54935          var center = this.mgr.getRegion("center");
54936          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
54937     },
54938
54939     getVMaxSize : function(){
54940          var cmax = this.config.maxSize || 10000;
54941          var center = this.mgr.getRegion("center");
54942          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
54943     },
54944
54945     onSplitMove : function(split, newSize){
54946         this.fireEvent("resized", this, newSize);
54947     },
54948     
54949     /** 
54950      * Returns the {@link Roo.SplitBar} for this region.
54951      * @return {Roo.SplitBar}
54952      */
54953     getSplitBar : function(){
54954         return this.split;
54955     },
54956     
54957     hide : function(){
54958         this.hideSplitter();
54959         Roo.SplitLayoutRegion.superclass.hide.call(this);
54960     },
54961
54962     hideSplitter : function(){
54963         if(this.split){
54964             this.split.el.setLocation(-2000,-2000);
54965             this.split.el.hide();
54966         }
54967     },
54968
54969     show : function(){
54970         if(this.split){
54971             this.split.el.show();
54972         }
54973         Roo.SplitLayoutRegion.superclass.show.call(this);
54974     },
54975     
54976     beforeSlide: function(){
54977         if(Roo.isGecko){// firefox overflow auto bug workaround
54978             this.bodyEl.clip();
54979             if(this.tabs) {
54980                 this.tabs.bodyEl.clip();
54981             }
54982             if(this.activePanel){
54983                 this.activePanel.getEl().clip();
54984                 
54985                 if(this.activePanel.beforeSlide){
54986                     this.activePanel.beforeSlide();
54987                 }
54988             }
54989         }
54990     },
54991     
54992     afterSlide : function(){
54993         if(Roo.isGecko){// firefox overflow auto bug workaround
54994             this.bodyEl.unclip();
54995             if(this.tabs) {
54996                 this.tabs.bodyEl.unclip();
54997             }
54998             if(this.activePanel){
54999                 this.activePanel.getEl().unclip();
55000                 if(this.activePanel.afterSlide){
55001                     this.activePanel.afterSlide();
55002                 }
55003             }
55004         }
55005     },
55006
55007     initAutoHide : function(){
55008         if(this.autoHide !== false){
55009             if(!this.autoHideHd){
55010                 var st = new Roo.util.DelayedTask(this.slideIn, this);
55011                 this.autoHideHd = {
55012                     "mouseout": function(e){
55013                         if(!e.within(this.el, true)){
55014                             st.delay(500);
55015                         }
55016                     },
55017                     "mouseover" : function(e){
55018                         st.cancel();
55019                     },
55020                     scope : this
55021                 };
55022             }
55023             this.el.on(this.autoHideHd);
55024         }
55025     },
55026
55027     clearAutoHide : function(){
55028         if(this.autoHide !== false){
55029             this.el.un("mouseout", this.autoHideHd.mouseout);
55030             this.el.un("mouseover", this.autoHideHd.mouseover);
55031         }
55032     },
55033
55034     clearMonitor : function(){
55035         Roo.get(document).un("click", this.slideInIf, this);
55036     },
55037
55038     // these names are backwards but not changed for compat
55039     slideOut : function(){
55040         if(this.isSlid || this.el.hasActiveFx()){
55041             return;
55042         }
55043         this.isSlid = true;
55044         if(this.collapseBtn){
55045             this.collapseBtn.hide();
55046         }
55047         this.closeBtnState = this.closeBtn.getStyle('display');
55048         this.closeBtn.hide();
55049         if(this.stickBtn){
55050             this.stickBtn.show();
55051         }
55052         this.el.show();
55053         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
55054         this.beforeSlide();
55055         this.el.setStyle("z-index", 10001);
55056         this.el.slideIn(this.getSlideAnchor(), {
55057             callback: function(){
55058                 this.afterSlide();
55059                 this.initAutoHide();
55060                 Roo.get(document).on("click", this.slideInIf, this);
55061                 this.fireEvent("slideshow", this);
55062             },
55063             scope: this,
55064             block: true
55065         });
55066     },
55067
55068     afterSlideIn : function(){
55069         this.clearAutoHide();
55070         this.isSlid = false;
55071         this.clearMonitor();
55072         this.el.setStyle("z-index", "");
55073         if(this.collapseBtn){
55074             this.collapseBtn.show();
55075         }
55076         this.closeBtn.setStyle('display', this.closeBtnState);
55077         if(this.stickBtn){
55078             this.stickBtn.hide();
55079         }
55080         this.fireEvent("slidehide", this);
55081     },
55082
55083     slideIn : function(cb){
55084         if(!this.isSlid || this.el.hasActiveFx()){
55085             Roo.callback(cb);
55086             return;
55087         }
55088         this.isSlid = false;
55089         this.beforeSlide();
55090         this.el.slideOut(this.getSlideAnchor(), {
55091             callback: function(){
55092                 this.el.setLeftTop(-10000, -10000);
55093                 this.afterSlide();
55094                 this.afterSlideIn();
55095                 Roo.callback(cb);
55096             },
55097             scope: this,
55098             block: true
55099         });
55100     },
55101     
55102     slideInIf : function(e){
55103         if(!e.within(this.el)){
55104             this.slideIn();
55105         }
55106     },
55107
55108     animateCollapse : function(){
55109         this.beforeSlide();
55110         this.el.setStyle("z-index", 20000);
55111         var anchor = this.getSlideAnchor();
55112         this.el.slideOut(anchor, {
55113             callback : function(){
55114                 this.el.setStyle("z-index", "");
55115                 this.collapsedEl.slideIn(anchor, {duration:.3});
55116                 this.afterSlide();
55117                 this.el.setLocation(-10000,-10000);
55118                 this.el.hide();
55119                 this.fireEvent("collapsed", this);
55120             },
55121             scope: this,
55122             block: true
55123         });
55124     },
55125
55126     animateExpand : function(){
55127         this.beforeSlide();
55128         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
55129         this.el.setStyle("z-index", 20000);
55130         this.collapsedEl.hide({
55131             duration:.1
55132         });
55133         this.el.slideIn(this.getSlideAnchor(), {
55134             callback : function(){
55135                 this.el.setStyle("z-index", "");
55136                 this.afterSlide();
55137                 if(this.split){
55138                     this.split.el.show();
55139                 }
55140                 this.fireEvent("invalidated", this);
55141                 this.fireEvent("expanded", this);
55142             },
55143             scope: this,
55144             block: true
55145         });
55146     },
55147
55148     anchors : {
55149         "west" : "left",
55150         "east" : "right",
55151         "north" : "top",
55152         "south" : "bottom"
55153     },
55154
55155     sanchors : {
55156         "west" : "l",
55157         "east" : "r",
55158         "north" : "t",
55159         "south" : "b"
55160     },
55161
55162     canchors : {
55163         "west" : "tl-tr",
55164         "east" : "tr-tl",
55165         "north" : "tl-bl",
55166         "south" : "bl-tl"
55167     },
55168
55169     getAnchor : function(){
55170         return this.anchors[this.position];
55171     },
55172
55173     getCollapseAnchor : function(){
55174         return this.canchors[this.position];
55175     },
55176
55177     getSlideAnchor : function(){
55178         return this.sanchors[this.position];
55179     },
55180
55181     getAlignAdj : function(){
55182         var cm = this.cmargins;
55183         switch(this.position){
55184             case "west":
55185                 return [0, 0];
55186             break;
55187             case "east":
55188                 return [0, 0];
55189             break;
55190             case "north":
55191                 return [0, 0];
55192             break;
55193             case "south":
55194                 return [0, 0];
55195             break;
55196         }
55197     },
55198
55199     getExpandAdj : function(){
55200         var c = this.collapsedEl, cm = this.cmargins;
55201         switch(this.position){
55202             case "west":
55203                 return [-(cm.right+c.getWidth()+cm.left), 0];
55204             break;
55205             case "east":
55206                 return [cm.right+c.getWidth()+cm.left, 0];
55207             break;
55208             case "north":
55209                 return [0, -(cm.top+cm.bottom+c.getHeight())];
55210             break;
55211             case "south":
55212                 return [0, cm.top+cm.bottom+c.getHeight()];
55213             break;
55214         }
55215     }
55216 });/*
55217  * Based on:
55218  * Ext JS Library 1.1.1
55219  * Copyright(c) 2006-2007, Ext JS, LLC.
55220  *
55221  * Originally Released Under LGPL - original licence link has changed is not relivant.
55222  *
55223  * Fork - LGPL
55224  * <script type="text/javascript">
55225  */
55226 /*
55227  * These classes are private internal classes
55228  */
55229 Roo.CenterLayoutRegion = function(mgr, config){
55230     Roo.LayoutRegion.call(this, mgr, config, "center");
55231     this.visible = true;
55232     this.minWidth = config.minWidth || 20;
55233     this.minHeight = config.minHeight || 20;
55234 };
55235
55236 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
55237     hide : function(){
55238         // center panel can't be hidden
55239     },
55240     
55241     show : function(){
55242         // center panel can't be hidden
55243     },
55244     
55245     getMinWidth: function(){
55246         return this.minWidth;
55247     },
55248     
55249     getMinHeight: function(){
55250         return this.minHeight;
55251     }
55252 });
55253
55254
55255 Roo.NorthLayoutRegion = function(mgr, config){
55256     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
55257     if(this.split){
55258         this.split.placement = Roo.SplitBar.TOP;
55259         this.split.orientation = Roo.SplitBar.VERTICAL;
55260         this.split.el.addClass("x-layout-split-v");
55261     }
55262     var size = config.initialSize || config.height;
55263     if(typeof size != "undefined"){
55264         this.el.setHeight(size);
55265     }
55266 };
55267 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
55268     orientation: Roo.SplitBar.VERTICAL,
55269     getBox : function(){
55270         if(this.collapsed){
55271             return this.collapsedEl.getBox();
55272         }
55273         var box = this.el.getBox();
55274         if(this.split){
55275             box.height += this.split.el.getHeight();
55276         }
55277         return box;
55278     },
55279     
55280     updateBox : function(box){
55281         if(this.split && !this.collapsed){
55282             box.height -= this.split.el.getHeight();
55283             this.split.el.setLeft(box.x);
55284             this.split.el.setTop(box.y+box.height);
55285             this.split.el.setWidth(box.width);
55286         }
55287         if(this.collapsed){
55288             this.updateBody(box.width, null);
55289         }
55290         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55291     }
55292 });
55293
55294 Roo.SouthLayoutRegion = function(mgr, config){
55295     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
55296     if(this.split){
55297         this.split.placement = Roo.SplitBar.BOTTOM;
55298         this.split.orientation = Roo.SplitBar.VERTICAL;
55299         this.split.el.addClass("x-layout-split-v");
55300     }
55301     var size = config.initialSize || config.height;
55302     if(typeof size != "undefined"){
55303         this.el.setHeight(size);
55304     }
55305 };
55306 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
55307     orientation: Roo.SplitBar.VERTICAL,
55308     getBox : function(){
55309         if(this.collapsed){
55310             return this.collapsedEl.getBox();
55311         }
55312         var box = this.el.getBox();
55313         if(this.split){
55314             var sh = this.split.el.getHeight();
55315             box.height += sh;
55316             box.y -= sh;
55317         }
55318         return box;
55319     },
55320     
55321     updateBox : function(box){
55322         if(this.split && !this.collapsed){
55323             var sh = this.split.el.getHeight();
55324             box.height -= sh;
55325             box.y += sh;
55326             this.split.el.setLeft(box.x);
55327             this.split.el.setTop(box.y-sh);
55328             this.split.el.setWidth(box.width);
55329         }
55330         if(this.collapsed){
55331             this.updateBody(box.width, null);
55332         }
55333         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55334     }
55335 });
55336
55337 Roo.EastLayoutRegion = function(mgr, config){
55338     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
55339     if(this.split){
55340         this.split.placement = Roo.SplitBar.RIGHT;
55341         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55342         this.split.el.addClass("x-layout-split-h");
55343     }
55344     var size = config.initialSize || config.width;
55345     if(typeof size != "undefined"){
55346         this.el.setWidth(size);
55347     }
55348 };
55349 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
55350     orientation: Roo.SplitBar.HORIZONTAL,
55351     getBox : function(){
55352         if(this.collapsed){
55353             return this.collapsedEl.getBox();
55354         }
55355         var box = this.el.getBox();
55356         if(this.split){
55357             var sw = this.split.el.getWidth();
55358             box.width += sw;
55359             box.x -= sw;
55360         }
55361         return box;
55362     },
55363
55364     updateBox : function(box){
55365         if(this.split && !this.collapsed){
55366             var sw = this.split.el.getWidth();
55367             box.width -= sw;
55368             this.split.el.setLeft(box.x);
55369             this.split.el.setTop(box.y);
55370             this.split.el.setHeight(box.height);
55371             box.x += sw;
55372         }
55373         if(this.collapsed){
55374             this.updateBody(null, box.height);
55375         }
55376         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55377     }
55378 });
55379
55380 Roo.WestLayoutRegion = function(mgr, config){
55381     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55382     if(this.split){
55383         this.split.placement = Roo.SplitBar.LEFT;
55384         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55385         this.split.el.addClass("x-layout-split-h");
55386     }
55387     var size = config.initialSize || config.width;
55388     if(typeof size != "undefined"){
55389         this.el.setWidth(size);
55390     }
55391 };
55392 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55393     orientation: Roo.SplitBar.HORIZONTAL,
55394     getBox : function(){
55395         if(this.collapsed){
55396             return this.collapsedEl.getBox();
55397         }
55398         var box = this.el.getBox();
55399         if(this.split){
55400             box.width += this.split.el.getWidth();
55401         }
55402         return box;
55403     },
55404     
55405     updateBox : function(box){
55406         if(this.split && !this.collapsed){
55407             var sw = this.split.el.getWidth();
55408             box.width -= sw;
55409             this.split.el.setLeft(box.x+box.width);
55410             this.split.el.setTop(box.y);
55411             this.split.el.setHeight(box.height);
55412         }
55413         if(this.collapsed){
55414             this.updateBody(null, box.height);
55415         }
55416         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55417     }
55418 });
55419 /*
55420  * Based on:
55421  * Ext JS Library 1.1.1
55422  * Copyright(c) 2006-2007, Ext JS, LLC.
55423  *
55424  * Originally Released Under LGPL - original licence link has changed is not relivant.
55425  *
55426  * Fork - LGPL
55427  * <script type="text/javascript">
55428  */
55429  
55430  
55431 /*
55432  * Private internal class for reading and applying state
55433  */
55434 Roo.LayoutStateManager = function(layout){
55435      // default empty state
55436      this.state = {
55437         north: {},
55438         south: {},
55439         east: {},
55440         west: {}       
55441     };
55442 };
55443
55444 Roo.LayoutStateManager.prototype = {
55445     init : function(layout, provider){
55446         this.provider = provider;
55447         var state = provider.get(layout.id+"-layout-state");
55448         if(state){
55449             var wasUpdating = layout.isUpdating();
55450             if(!wasUpdating){
55451                 layout.beginUpdate();
55452             }
55453             for(var key in state){
55454                 if(typeof state[key] != "function"){
55455                     var rstate = state[key];
55456                     var r = layout.getRegion(key);
55457                     if(r && rstate){
55458                         if(rstate.size){
55459                             r.resizeTo(rstate.size);
55460                         }
55461                         if(rstate.collapsed == true){
55462                             r.collapse(true);
55463                         }else{
55464                             r.expand(null, true);
55465                         }
55466                     }
55467                 }
55468             }
55469             if(!wasUpdating){
55470                 layout.endUpdate();
55471             }
55472             this.state = state; 
55473         }
55474         this.layout = layout;
55475         layout.on("regionresized", this.onRegionResized, this);
55476         layout.on("regioncollapsed", this.onRegionCollapsed, this);
55477         layout.on("regionexpanded", this.onRegionExpanded, this);
55478     },
55479     
55480     storeState : function(){
55481         this.provider.set(this.layout.id+"-layout-state", this.state);
55482     },
55483     
55484     onRegionResized : function(region, newSize){
55485         this.state[region.getPosition()].size = newSize;
55486         this.storeState();
55487     },
55488     
55489     onRegionCollapsed : function(region){
55490         this.state[region.getPosition()].collapsed = true;
55491         this.storeState();
55492     },
55493     
55494     onRegionExpanded : function(region){
55495         this.state[region.getPosition()].collapsed = false;
55496         this.storeState();
55497     }
55498 };/*
55499  * Based on:
55500  * Ext JS Library 1.1.1
55501  * Copyright(c) 2006-2007, Ext JS, LLC.
55502  *
55503  * Originally Released Under LGPL - original licence link has changed is not relivant.
55504  *
55505  * Fork - LGPL
55506  * <script type="text/javascript">
55507  */
55508 /**
55509  * @class Roo.ContentPanel
55510  * @extends Roo.util.Observable
55511  * @children Roo.form.Form Roo.JsonView Roo.View
55512  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
55513  * A basic ContentPanel element.
55514  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
55515  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
55516  * @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
55517  * @cfg {Boolean}   closable      True if the panel can be closed/removed
55518  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
55519  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
55520  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
55521  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
55522  * @cfg {String} title          The title for this panel
55523  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
55524  * @cfg {String} url            Calls {@link #setUrl} with this value
55525  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
55526  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
55527  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
55528  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
55529  * @cfg {String}    style  Extra style to add to the content panel
55530  * @cfg {Roo.menu.Menu} menu  popup menu
55531
55532  * @constructor
55533  * Create a new ContentPanel.
55534  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
55535  * @param {String/Object} config A string to set only the title or a config object
55536  * @param {String} content (optional) Set the HTML content for this panel
55537  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
55538  */
55539 Roo.ContentPanel = function(el, config, content){
55540     
55541      
55542     /*
55543     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
55544         config = el;
55545         el = Roo.id();
55546     }
55547     if (config && config.parentLayout) { 
55548         el = config.parentLayout.el.createChild(); 
55549     }
55550     */
55551     if(el.autoCreate){ // xtype is available if this is called from factory
55552         config = el;
55553         el = Roo.id();
55554     }
55555     this.el = Roo.get(el);
55556     if(!this.el && config && config.autoCreate){
55557         if(typeof config.autoCreate == "object"){
55558             if(!config.autoCreate.id){
55559                 config.autoCreate.id = config.id||el;
55560             }
55561             this.el = Roo.DomHelper.append(document.body,
55562                         config.autoCreate, true);
55563         }else{
55564             this.el = Roo.DomHelper.append(document.body,
55565                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
55566         }
55567     }
55568     
55569     
55570     this.closable = false;
55571     this.loaded = false;
55572     this.active = false;
55573     if(typeof config == "string"){
55574         this.title = config;
55575     }else{
55576         Roo.apply(this, config);
55577     }
55578     
55579     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
55580         this.wrapEl = this.el.wrap();
55581         this.toolbar.container = this.el.insertSibling(false, 'before');
55582         this.toolbar = new Roo.Toolbar(this.toolbar);
55583     }
55584     
55585     // xtype created footer. - not sure if will work as we normally have to render first..
55586     if (this.footer && !this.footer.el && this.footer.xtype) {
55587         if (!this.wrapEl) {
55588             this.wrapEl = this.el.wrap();
55589         }
55590     
55591         this.footer.container = this.wrapEl.createChild();
55592          
55593         this.footer = Roo.factory(this.footer, Roo);
55594         
55595     }
55596     
55597     if(this.resizeEl){
55598         this.resizeEl = Roo.get(this.resizeEl, true);
55599     }else{
55600         this.resizeEl = this.el;
55601     }
55602     // handle view.xtype
55603     
55604  
55605     
55606     
55607     this.addEvents({
55608         /**
55609          * @event activate
55610          * Fires when this panel is activated. 
55611          * @param {Roo.ContentPanel} this
55612          */
55613         "activate" : true,
55614         /**
55615          * @event deactivate
55616          * Fires when this panel is activated. 
55617          * @param {Roo.ContentPanel} this
55618          */
55619         "deactivate" : true,
55620
55621         /**
55622          * @event resize
55623          * Fires when this panel is resized if fitToFrame is true.
55624          * @param {Roo.ContentPanel} this
55625          * @param {Number} width The width after any component adjustments
55626          * @param {Number} height The height after any component adjustments
55627          */
55628         "resize" : true,
55629         
55630          /**
55631          * @event render
55632          * Fires when this tab is created
55633          * @param {Roo.ContentPanel} this
55634          */
55635         "render" : true
55636          
55637         
55638     });
55639     
55640
55641     
55642     
55643     if(this.autoScroll){
55644         this.resizeEl.setStyle("overflow", "auto");
55645     } else {
55646         // fix randome scrolling
55647         this.el.on('scroll', function() {
55648             Roo.log('fix random scolling');
55649             this.scrollTo('top',0); 
55650         });
55651     }
55652     content = content || this.content;
55653     if(content){
55654         this.setContent(content);
55655     }
55656     if(config && config.url){
55657         this.setUrl(this.url, this.params, this.loadOnce);
55658     }
55659     
55660     
55661     
55662     Roo.ContentPanel.superclass.constructor.call(this);
55663     
55664     if (this.view && typeof(this.view.xtype) != 'undefined') {
55665         this.view.el = this.el.appendChild(document.createElement("div"));
55666         this.view = Roo.factory(this.view); 
55667         this.view.render  &&  this.view.render(false, '');  
55668     }
55669     
55670     
55671     this.fireEvent('render', this);
55672 };
55673
55674 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
55675     tabTip:'',
55676     setRegion : function(region){
55677         this.region = region;
55678         if(region){
55679            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
55680         }else{
55681            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
55682         } 
55683     },
55684     
55685     /**
55686      * Returns the toolbar for this Panel if one was configured. 
55687      * @return {Roo.Toolbar} 
55688      */
55689     getToolbar : function(){
55690         return this.toolbar;
55691     },
55692     
55693     setActiveState : function(active){
55694         this.active = active;
55695         if(!active){
55696             this.fireEvent("deactivate", this);
55697         }else{
55698             this.fireEvent("activate", this);
55699         }
55700     },
55701     /**
55702      * Updates this panel's element
55703      * @param {String} content The new content
55704      * @param {Boolean} loadScripts (optional) true to look for and process scripts
55705     */
55706     setContent : function(content, loadScripts){
55707         this.el.update(content, loadScripts);
55708     },
55709
55710     ignoreResize : function(w, h){
55711         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
55712             return true;
55713         }else{
55714             this.lastSize = {width: w, height: h};
55715             return false;
55716         }
55717     },
55718     /**
55719      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
55720      * @return {Roo.UpdateManager} The UpdateManager
55721      */
55722     getUpdateManager : function(){
55723         return this.el.getUpdateManager();
55724     },
55725      /**
55726      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
55727      * @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:
55728 <pre><code>
55729 panel.load({
55730     url: "your-url.php",
55731     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
55732     callback: yourFunction,
55733     scope: yourObject, //(optional scope)
55734     discardUrl: false,
55735     nocache: false,
55736     text: "Loading...",
55737     timeout: 30,
55738     scripts: false
55739 });
55740 </code></pre>
55741      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
55742      * 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.
55743      * @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}
55744      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
55745      * @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.
55746      * @return {Roo.ContentPanel} this
55747      */
55748     load : function(){
55749         var um = this.el.getUpdateManager();
55750         um.update.apply(um, arguments);
55751         return this;
55752     },
55753
55754
55755     /**
55756      * 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.
55757      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
55758      * @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)
55759      * @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)
55760      * @return {Roo.UpdateManager} The UpdateManager
55761      */
55762     setUrl : function(url, params, loadOnce){
55763         if(this.refreshDelegate){
55764             this.removeListener("activate", this.refreshDelegate);
55765         }
55766         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
55767         this.on("activate", this.refreshDelegate);
55768         return this.el.getUpdateManager();
55769     },
55770     
55771     _handleRefresh : function(url, params, loadOnce){
55772         if(!loadOnce || !this.loaded){
55773             var updater = this.el.getUpdateManager();
55774             updater.update(url, params, this._setLoaded.createDelegate(this));
55775         }
55776     },
55777     
55778     _setLoaded : function(){
55779         this.loaded = true;
55780     }, 
55781     
55782     /**
55783      * Returns this panel's id
55784      * @return {String} 
55785      */
55786     getId : function(){
55787         return this.el.id;
55788     },
55789     
55790     /** 
55791      * Returns this panel's element - used by regiosn to add.
55792      * @return {Roo.Element} 
55793      */
55794     getEl : function(){
55795         return this.wrapEl || this.el;
55796     },
55797     
55798     adjustForComponents : function(width, height)
55799     {
55800         //Roo.log('adjustForComponents ');
55801         if(this.resizeEl != this.el){
55802             width -= this.el.getFrameWidth('lr');
55803             height -= this.el.getFrameWidth('tb');
55804         }
55805         if(this.toolbar){
55806             var te = this.toolbar.getEl();
55807             height -= te.getHeight();
55808             te.setWidth(width);
55809         }
55810         if(this.footer){
55811             var te = this.footer.getEl();
55812             //Roo.log("footer:" + te.getHeight());
55813             
55814             height -= te.getHeight();
55815             te.setWidth(width);
55816         }
55817         
55818         
55819         if(this.adjustments){
55820             width += this.adjustments[0];
55821             height += this.adjustments[1];
55822         }
55823         return {"width": width, "height": height};
55824     },
55825     
55826     setSize : function(width, height){
55827         if(this.fitToFrame && !this.ignoreResize(width, height)){
55828             if(this.fitContainer && this.resizeEl != this.el){
55829                 this.el.setSize(width, height);
55830             }
55831             var size = this.adjustForComponents(width, height);
55832             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
55833             this.fireEvent('resize', this, size.width, size.height);
55834         }
55835     },
55836     
55837     /**
55838      * Returns this panel's title
55839      * @return {String} 
55840      */
55841     getTitle : function(){
55842         return this.title;
55843     },
55844     
55845     /**
55846      * Set this panel's title
55847      * @param {String} title
55848      */
55849     setTitle : function(title){
55850         this.title = title;
55851         if(this.region){
55852             this.region.updatePanelTitle(this, title);
55853         }
55854     },
55855     
55856     /**
55857      * Returns true is this panel was configured to be closable
55858      * @return {Boolean} 
55859      */
55860     isClosable : function(){
55861         return this.closable;
55862     },
55863     
55864     beforeSlide : function(){
55865         this.el.clip();
55866         this.resizeEl.clip();
55867     },
55868     
55869     afterSlide : function(){
55870         this.el.unclip();
55871         this.resizeEl.unclip();
55872     },
55873     
55874     /**
55875      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
55876      *   Will fail silently if the {@link #setUrl} method has not been called.
55877      *   This does not activate the panel, just updates its content.
55878      */
55879     refresh : function(){
55880         if(this.refreshDelegate){
55881            this.loaded = false;
55882            this.refreshDelegate();
55883         }
55884     },
55885     
55886     /**
55887      * Destroys this panel
55888      */
55889     destroy : function(){
55890         this.el.removeAllListeners();
55891         var tempEl = document.createElement("span");
55892         tempEl.appendChild(this.el.dom);
55893         tempEl.innerHTML = "";
55894         this.el.remove();
55895         this.el = null;
55896     },
55897     
55898     /**
55899      * form - if the content panel contains a form - this is a reference to it.
55900      * @type {Roo.form.Form}
55901      */
55902     form : false,
55903     /**
55904      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
55905      *    This contains a reference to it.
55906      * @type {Roo.View}
55907      */
55908     view : false,
55909     
55910       /**
55911      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
55912      * <pre><code>
55913
55914 layout.addxtype({
55915        xtype : 'Form',
55916        items: [ .... ]
55917    }
55918 );
55919
55920 </code></pre>
55921      * @param {Object} cfg Xtype definition of item to add.
55922      */
55923     
55924     addxtype : function(cfg) {
55925         // add form..
55926         if (cfg.xtype.match(/^Form$/)) {
55927             
55928             var el;
55929             //if (this.footer) {
55930             //    el = this.footer.container.insertSibling(false, 'before');
55931             //} else {
55932                 el = this.el.createChild();
55933             //}
55934
55935             this.form = new  Roo.form.Form(cfg);
55936             
55937             
55938             if ( this.form.allItems.length) {
55939                 this.form.render(el.dom);
55940             }
55941             return this.form;
55942         }
55943         // should only have one of theses..
55944         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
55945             // views.. should not be just added - used named prop 'view''
55946             
55947             cfg.el = this.el.appendChild(document.createElement("div"));
55948             // factory?
55949             
55950             var ret = new Roo.factory(cfg);
55951              
55952              ret.render && ret.render(false, ''); // render blank..
55953             this.view = ret;
55954             return ret;
55955         }
55956         return false;
55957     }
55958 });
55959
55960 /**
55961  * @class Roo.GridPanel
55962  * @extends Roo.ContentPanel
55963  * @constructor
55964  * Create a new GridPanel.
55965  * @param {Roo.grid.Grid} grid The grid for this panel
55966  * @param {String/Object} config A string to set only the panel's title, or a config object
55967  */
55968 Roo.GridPanel = function(grid, config){
55969     
55970   
55971     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
55972         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
55973         
55974     this.wrapper.dom.appendChild(grid.getGridEl().dom);
55975     
55976     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
55977     
55978     if(this.toolbar){
55979         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
55980     }
55981     // xtype created footer. - not sure if will work as we normally have to render first..
55982     if (this.footer && !this.footer.el && this.footer.xtype) {
55983         
55984         this.footer.container = this.grid.getView().getFooterPanel(true);
55985         this.footer.dataSource = this.grid.dataSource;
55986         this.footer = Roo.factory(this.footer, Roo);
55987         
55988     }
55989     
55990     grid.monitorWindowResize = false; // turn off autosizing
55991     grid.autoHeight = false;
55992     grid.autoWidth = false;
55993     this.grid = grid;
55994     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
55995 };
55996
55997 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
55998     getId : function(){
55999         return this.grid.id;
56000     },
56001     
56002     /**
56003      * Returns the grid for this panel
56004      * @return {Roo.grid.Grid} 
56005      */
56006     getGrid : function(){
56007         return this.grid;    
56008     },
56009     
56010     setSize : function(width, height){
56011         if(!this.ignoreResize(width, height)){
56012             var grid = this.grid;
56013             var size = this.adjustForComponents(width, height);
56014             grid.getGridEl().setSize(size.width, size.height);
56015             grid.autoSize();
56016         }
56017     },
56018     
56019     beforeSlide : function(){
56020         this.grid.getView().scroller.clip();
56021     },
56022     
56023     afterSlide : function(){
56024         this.grid.getView().scroller.unclip();
56025     },
56026     
56027     destroy : function(){
56028         this.grid.destroy();
56029         delete this.grid;
56030         Roo.GridPanel.superclass.destroy.call(this); 
56031     }
56032 });
56033
56034
56035 /**
56036  * @class Roo.NestedLayoutPanel
56037  * @extends Roo.ContentPanel
56038  * @constructor
56039  * Create a new NestedLayoutPanel.
56040  * 
56041  * 
56042  * @param {Roo.BorderLayout} layout [required] The layout for this panel
56043  * @param {String/Object} config A string to set only the title or a config object
56044  */
56045 Roo.NestedLayoutPanel = function(layout, config)
56046 {
56047     // construct with only one argument..
56048     /* FIXME - implement nicer consturctors
56049     if (layout.layout) {
56050         config = layout;
56051         layout = config.layout;
56052         delete config.layout;
56053     }
56054     if (layout.xtype && !layout.getEl) {
56055         // then layout needs constructing..
56056         layout = Roo.factory(layout, Roo);
56057     }
56058     */
56059     
56060     
56061     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
56062     
56063     layout.monitorWindowResize = false; // turn off autosizing
56064     this.layout = layout;
56065     this.layout.getEl().addClass("x-layout-nested-layout");
56066     
56067     
56068     
56069     
56070 };
56071
56072 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
56073
56074     setSize : function(width, height){
56075         if(!this.ignoreResize(width, height)){
56076             var size = this.adjustForComponents(width, height);
56077             var el = this.layout.getEl();
56078             el.setSize(size.width, size.height);
56079             var touch = el.dom.offsetWidth;
56080             this.layout.layout();
56081             // ie requires a double layout on the first pass
56082             if(Roo.isIE && !this.initialized){
56083                 this.initialized = true;
56084                 this.layout.layout();
56085             }
56086         }
56087     },
56088     
56089     // activate all subpanels if not currently active..
56090     
56091     setActiveState : function(active){
56092         this.active = active;
56093         if(!active){
56094             this.fireEvent("deactivate", this);
56095             return;
56096         }
56097         
56098         this.fireEvent("activate", this);
56099         // not sure if this should happen before or after..
56100         if (!this.layout) {
56101             return; // should not happen..
56102         }
56103         var reg = false;
56104         for (var r in this.layout.regions) {
56105             reg = this.layout.getRegion(r);
56106             if (reg.getActivePanel()) {
56107                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
56108                 reg.setActivePanel(reg.getActivePanel());
56109                 continue;
56110             }
56111             if (!reg.panels.length) {
56112                 continue;
56113             }
56114             reg.showPanel(reg.getPanel(0));
56115         }
56116         
56117         
56118         
56119         
56120     },
56121     
56122     /**
56123      * Returns the nested BorderLayout for this panel
56124      * @return {Roo.BorderLayout} 
56125      */
56126     getLayout : function(){
56127         return this.layout;
56128     },
56129     
56130      /**
56131      * Adds a xtype elements to the layout of the nested panel
56132      * <pre><code>
56133
56134 panel.addxtype({
56135        xtype : 'ContentPanel',
56136        region: 'west',
56137        items: [ .... ]
56138    }
56139 );
56140
56141 panel.addxtype({
56142         xtype : 'NestedLayoutPanel',
56143         region: 'west',
56144         layout: {
56145            center: { },
56146            west: { }   
56147         },
56148         items : [ ... list of content panels or nested layout panels.. ]
56149    }
56150 );
56151 </code></pre>
56152      * @param {Object} cfg Xtype definition of item to add.
56153      */
56154     addxtype : function(cfg) {
56155         return this.layout.addxtype(cfg);
56156     
56157     }
56158 });
56159
56160 Roo.ScrollPanel = function(el, config, content){
56161     config = config || {};
56162     config.fitToFrame = true;
56163     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
56164     
56165     this.el.dom.style.overflow = "hidden";
56166     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
56167     this.el.removeClass("x-layout-inactive-content");
56168     this.el.on("mousewheel", this.onWheel, this);
56169
56170     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
56171     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
56172     up.unselectable(); down.unselectable();
56173     up.on("click", this.scrollUp, this);
56174     down.on("click", this.scrollDown, this);
56175     up.addClassOnOver("x-scroller-btn-over");
56176     down.addClassOnOver("x-scroller-btn-over");
56177     up.addClassOnClick("x-scroller-btn-click");
56178     down.addClassOnClick("x-scroller-btn-click");
56179     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
56180
56181     this.resizeEl = this.el;
56182     this.el = wrap; this.up = up; this.down = down;
56183 };
56184
56185 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
56186     increment : 100,
56187     wheelIncrement : 5,
56188     scrollUp : function(){
56189         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
56190     },
56191
56192     scrollDown : function(){
56193         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
56194     },
56195
56196     afterScroll : function(){
56197         var el = this.resizeEl;
56198         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
56199         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56200         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56201     },
56202
56203     setSize : function(){
56204         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
56205         this.afterScroll();
56206     },
56207
56208     onWheel : function(e){
56209         var d = e.getWheelDelta();
56210         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
56211         this.afterScroll();
56212         e.stopEvent();
56213     },
56214
56215     setContent : function(content, loadScripts){
56216         this.resizeEl.update(content, loadScripts);
56217     }
56218
56219 });
56220
56221
56222
56223 /**
56224  * @class Roo.TreePanel
56225  * @extends Roo.ContentPanel
56226  * Treepanel component
56227  * 
56228  * @constructor
56229  * Create a new TreePanel. - defaults to fit/scoll contents.
56230  * @param {String/Object} config A string to set only the panel's title, or a config object
56231  */
56232 Roo.TreePanel = function(config){
56233     var el = config.el;
56234     var tree = config.tree;
56235     delete config.tree; 
56236     delete config.el; // hopefull!
56237     
56238     // wrapper for IE7 strict & safari scroll issue
56239     
56240     var treeEl = el.createChild();
56241     config.resizeEl = treeEl;
56242     
56243     
56244     
56245     Roo.TreePanel.superclass.constructor.call(this, el, config);
56246  
56247  
56248     this.tree = new Roo.tree.TreePanel(treeEl , tree);
56249     //console.log(tree);
56250     this.on('activate', function()
56251     {
56252         if (this.tree.rendered) {
56253             return;
56254         }
56255         //console.log('render tree');
56256         this.tree.render();
56257     });
56258     // this should not be needed.. - it's actually the 'el' that resizes?
56259     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
56260     
56261     //this.on('resize',  function (cp, w, h) {
56262     //        this.tree.innerCt.setWidth(w);
56263     //        this.tree.innerCt.setHeight(h);
56264     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
56265     //});
56266
56267         
56268     
56269 };
56270
56271 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
56272     fitToFrame : true,
56273     autoScroll : true,
56274     /*
56275      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
56276      */
56277     tree : false
56278
56279 });
56280
56281
56282
56283
56284
56285
56286
56287
56288
56289
56290
56291 /*
56292  * Based on:
56293  * Ext JS Library 1.1.1
56294  * Copyright(c) 2006-2007, Ext JS, LLC.
56295  *
56296  * Originally Released Under LGPL - original licence link has changed is not relivant.
56297  *
56298  * Fork - LGPL
56299  * <script type="text/javascript">
56300  */
56301  
56302
56303 /**
56304  * @class Roo.ReaderLayout
56305  * @extends Roo.BorderLayout
56306  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
56307  * center region containing two nested regions (a top one for a list view and one for item preview below),
56308  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
56309  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
56310  * expedites the setup of the overall layout and regions for this common application style.
56311  * Example:
56312  <pre><code>
56313 var reader = new Roo.ReaderLayout();
56314 var CP = Roo.ContentPanel;  // shortcut for adding
56315
56316 reader.beginUpdate();
56317 reader.add("north", new CP("north", "North"));
56318 reader.add("west", new CP("west", {title: "West"}));
56319 reader.add("east", new CP("east", {title: "East"}));
56320
56321 reader.regions.listView.add(new CP("listView", "List"));
56322 reader.regions.preview.add(new CP("preview", "Preview"));
56323 reader.endUpdate();
56324 </code></pre>
56325 * @constructor
56326 * Create a new ReaderLayout
56327 * @param {Object} config Configuration options
56328 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
56329 * document.body if omitted)
56330 */
56331 Roo.ReaderLayout = function(config, renderTo){
56332     var c = config || {size:{}};
56333     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
56334         north: c.north !== false ? Roo.apply({
56335             split:false,
56336             initialSize: 32,
56337             titlebar: false
56338         }, c.north) : false,
56339         west: c.west !== false ? Roo.apply({
56340             split:true,
56341             initialSize: 200,
56342             minSize: 175,
56343             maxSize: 400,
56344             titlebar: true,
56345             collapsible: true,
56346             animate: true,
56347             margins:{left:5,right:0,bottom:5,top:5},
56348             cmargins:{left:5,right:5,bottom:5,top:5}
56349         }, c.west) : false,
56350         east: c.east !== false ? Roo.apply({
56351             split:true,
56352             initialSize: 200,
56353             minSize: 175,
56354             maxSize: 400,
56355             titlebar: true,
56356             collapsible: true,
56357             animate: true,
56358             margins:{left:0,right:5,bottom:5,top:5},
56359             cmargins:{left:5,right:5,bottom:5,top:5}
56360         }, c.east) : false,
56361         center: Roo.apply({
56362             tabPosition: 'top',
56363             autoScroll:false,
56364             closeOnTab: true,
56365             titlebar:false,
56366             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56367         }, c.center)
56368     });
56369
56370     this.el.addClass('x-reader');
56371
56372     this.beginUpdate();
56373
56374     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56375         south: c.preview !== false ? Roo.apply({
56376             split:true,
56377             initialSize: 200,
56378             minSize: 100,
56379             autoScroll:true,
56380             collapsible:true,
56381             titlebar: true,
56382             cmargins:{top:5,left:0, right:0, bottom:0}
56383         }, c.preview) : false,
56384         center: Roo.apply({
56385             autoScroll:false,
56386             titlebar:false,
56387             minHeight:200
56388         }, c.listView)
56389     });
56390     this.add('center', new Roo.NestedLayoutPanel(inner,
56391             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56392
56393     this.endUpdate();
56394
56395     this.regions.preview = inner.getRegion('south');
56396     this.regions.listView = inner.getRegion('center');
56397 };
56398
56399 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56400  * Based on:
56401  * Ext JS Library 1.1.1
56402  * Copyright(c) 2006-2007, Ext JS, LLC.
56403  *
56404  * Originally Released Under LGPL - original licence link has changed is not relivant.
56405  *
56406  * Fork - LGPL
56407  * <script type="text/javascript">
56408  */
56409  
56410 /**
56411  * @class Roo.grid.Grid
56412  * @extends Roo.util.Observable
56413  * This class represents the primary interface of a component based grid control.
56414  * <br><br>Usage:<pre><code>
56415  var grid = new Roo.grid.Grid("my-container-id", {
56416      ds: myDataStore,
56417      cm: myColModel,
56418      selModel: mySelectionModel,
56419      autoSizeColumns: true,
56420      monitorWindowResize: false,
56421      trackMouseOver: true
56422  });
56423  // set any options
56424  grid.render();
56425  * </code></pre>
56426  * <b>Common Problems:</b><br/>
56427  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56428  * element will correct this<br/>
56429  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56430  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56431  * are unpredictable.<br/>
56432  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56433  * grid to calculate dimensions/offsets.<br/>
56434   * @constructor
56435  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56436  * The container MUST have some type of size defined for the grid to fill. The container will be
56437  * automatically set to position relative if it isn't already.
56438  * @param {Object} config A config object that sets properties on this grid.
56439  */
56440 Roo.grid.Grid = function(container, config){
56441         // initialize the container
56442         this.container = Roo.get(container);
56443         this.container.update("");
56444         this.container.setStyle("overflow", "hidden");
56445     this.container.addClass('x-grid-container');
56446
56447     this.id = this.container.id;
56448
56449     Roo.apply(this, config);
56450     // check and correct shorthanded configs
56451     if(this.ds){
56452         this.dataSource = this.ds;
56453         delete this.ds;
56454     }
56455     if(this.cm){
56456         this.colModel = this.cm;
56457         delete this.cm;
56458     }
56459     if(this.sm){
56460         this.selModel = this.sm;
56461         delete this.sm;
56462     }
56463
56464     if (this.selModel) {
56465         this.selModel = Roo.factory(this.selModel, Roo.grid);
56466         this.sm = this.selModel;
56467         this.sm.xmodule = this.xmodule || false;
56468     }
56469     if (typeof(this.colModel.config) == 'undefined') {
56470         this.colModel = new Roo.grid.ColumnModel(this.colModel);
56471         this.cm = this.colModel;
56472         this.cm.xmodule = this.xmodule || false;
56473     }
56474     if (this.dataSource) {
56475         this.dataSource= Roo.factory(this.dataSource, Roo.data);
56476         this.ds = this.dataSource;
56477         this.ds.xmodule = this.xmodule || false;
56478          
56479     }
56480     
56481     
56482     
56483     if(this.width){
56484         this.container.setWidth(this.width);
56485     }
56486
56487     if(this.height){
56488         this.container.setHeight(this.height);
56489     }
56490     /** @private */
56491         this.addEvents({
56492         // raw events
56493         /**
56494          * @event click
56495          * The raw click event for the entire grid.
56496          * @param {Roo.EventObject} e
56497          */
56498         "click" : true,
56499         /**
56500          * @event dblclick
56501          * The raw dblclick event for the entire grid.
56502          * @param {Roo.EventObject} e
56503          */
56504         "dblclick" : true,
56505         /**
56506          * @event contextmenu
56507          * The raw contextmenu event for the entire grid.
56508          * @param {Roo.EventObject} e
56509          */
56510         "contextmenu" : true,
56511         /**
56512          * @event mousedown
56513          * The raw mousedown event for the entire grid.
56514          * @param {Roo.EventObject} e
56515          */
56516         "mousedown" : true,
56517         /**
56518          * @event mouseup
56519          * The raw mouseup event for the entire grid.
56520          * @param {Roo.EventObject} e
56521          */
56522         "mouseup" : true,
56523         /**
56524          * @event mouseover
56525          * The raw mouseover event for the entire grid.
56526          * @param {Roo.EventObject} e
56527          */
56528         "mouseover" : true,
56529         /**
56530          * @event mouseout
56531          * The raw mouseout event for the entire grid.
56532          * @param {Roo.EventObject} e
56533          */
56534         "mouseout" : true,
56535         /**
56536          * @event keypress
56537          * The raw keypress event for the entire grid.
56538          * @param {Roo.EventObject} e
56539          */
56540         "keypress" : true,
56541         /**
56542          * @event keydown
56543          * The raw keydown event for the entire grid.
56544          * @param {Roo.EventObject} e
56545          */
56546         "keydown" : true,
56547
56548         // custom events
56549
56550         /**
56551          * @event cellclick
56552          * Fires when a cell is clicked
56553          * @param {Grid} this
56554          * @param {Number} rowIndex
56555          * @param {Number} columnIndex
56556          * @param {Roo.EventObject} e
56557          */
56558         "cellclick" : true,
56559         /**
56560          * @event celldblclick
56561          * Fires when a cell is double clicked
56562          * @param {Grid} this
56563          * @param {Number} rowIndex
56564          * @param {Number} columnIndex
56565          * @param {Roo.EventObject} e
56566          */
56567         "celldblclick" : true,
56568         /**
56569          * @event rowclick
56570          * Fires when a row is clicked
56571          * @param {Grid} this
56572          * @param {Number} rowIndex
56573          * @param {Roo.EventObject} e
56574          */
56575         "rowclick" : true,
56576         /**
56577          * @event rowdblclick
56578          * Fires when a row is double clicked
56579          * @param {Grid} this
56580          * @param {Number} rowIndex
56581          * @param {Roo.EventObject} e
56582          */
56583         "rowdblclick" : true,
56584         /**
56585          * @event headerclick
56586          * Fires when a header is clicked
56587          * @param {Grid} this
56588          * @param {Number} columnIndex
56589          * @param {Roo.EventObject} e
56590          */
56591         "headerclick" : true,
56592         /**
56593          * @event headerdblclick
56594          * Fires when a header cell is double clicked
56595          * @param {Grid} this
56596          * @param {Number} columnIndex
56597          * @param {Roo.EventObject} e
56598          */
56599         "headerdblclick" : true,
56600         /**
56601          * @event rowcontextmenu
56602          * Fires when a row is right clicked
56603          * @param {Grid} this
56604          * @param {Number} rowIndex
56605          * @param {Roo.EventObject} e
56606          */
56607         "rowcontextmenu" : true,
56608         /**
56609          * @event cellcontextmenu
56610          * Fires when a cell is right clicked
56611          * @param {Grid} this
56612          * @param {Number} rowIndex
56613          * @param {Number} cellIndex
56614          * @param {Roo.EventObject} e
56615          */
56616          "cellcontextmenu" : true,
56617         /**
56618          * @event headercontextmenu
56619          * Fires when a header is right clicked
56620          * @param {Grid} this
56621          * @param {Number} columnIndex
56622          * @param {Roo.EventObject} e
56623          */
56624         "headercontextmenu" : true,
56625         /**
56626          * @event bodyscroll
56627          * Fires when the body element is scrolled
56628          * @param {Number} scrollLeft
56629          * @param {Number} scrollTop
56630          */
56631         "bodyscroll" : true,
56632         /**
56633          * @event columnresize
56634          * Fires when the user resizes a column
56635          * @param {Number} columnIndex
56636          * @param {Number} newSize
56637          */
56638         "columnresize" : true,
56639         /**
56640          * @event columnmove
56641          * Fires when the user moves a column
56642          * @param {Number} oldIndex
56643          * @param {Number} newIndex
56644          */
56645         "columnmove" : true,
56646         /**
56647          * @event startdrag
56648          * Fires when row(s) start being dragged
56649          * @param {Grid} this
56650          * @param {Roo.GridDD} dd The drag drop object
56651          * @param {event} e The raw browser event
56652          */
56653         "startdrag" : true,
56654         /**
56655          * @event enddrag
56656          * Fires when a drag operation is complete
56657          * @param {Grid} this
56658          * @param {Roo.GridDD} dd The drag drop object
56659          * @param {event} e The raw browser event
56660          */
56661         "enddrag" : true,
56662         /**
56663          * @event dragdrop
56664          * Fires when dragged row(s) are dropped on a valid DD target
56665          * @param {Grid} this
56666          * @param {Roo.GridDD} dd The drag drop object
56667          * @param {String} targetId The target drag drop object
56668          * @param {event} e The raw browser event
56669          */
56670         "dragdrop" : true,
56671         /**
56672          * @event dragover
56673          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56674          * @param {Grid} this
56675          * @param {Roo.GridDD} dd The drag drop object
56676          * @param {String} targetId The target drag drop object
56677          * @param {event} e The raw browser event
56678          */
56679         "dragover" : true,
56680         /**
56681          * @event dragenter
56682          *  Fires when the dragged row(s) first cross another DD target while being dragged
56683          * @param {Grid} this
56684          * @param {Roo.GridDD} dd The drag drop object
56685          * @param {String} targetId The target drag drop object
56686          * @param {event} e The raw browser event
56687          */
56688         "dragenter" : true,
56689         /**
56690          * @event dragout
56691          * Fires when the dragged row(s) leave another DD target while being dragged
56692          * @param {Grid} this
56693          * @param {Roo.GridDD} dd The drag drop object
56694          * @param {String} targetId The target drag drop object
56695          * @param {event} e The raw browser event
56696          */
56697         "dragout" : true,
56698         /**
56699          * @event rowclass
56700          * Fires when a row is rendered, so you can change add a style to it.
56701          * @param {GridView} gridview   The grid view
56702          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56703          */
56704         'rowclass' : true,
56705
56706         /**
56707          * @event render
56708          * Fires when the grid is rendered
56709          * @param {Grid} grid
56710          */
56711         'render' : true
56712     });
56713
56714     Roo.grid.Grid.superclass.constructor.call(this);
56715 };
56716 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
56717     
56718     /**
56719          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
56720          */
56721         /**
56722          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
56723          */
56724         /**
56725          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
56726          */
56727         /**
56728          * @cfg {Roo.grid.Store} ds The data store for the grid
56729          */
56730         /**
56731          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
56732          */
56733         /**
56734      * @cfg {String} ddGroup - drag drop group.
56735      */
56736       /**
56737      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
56738      */
56739
56740     /**
56741      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
56742      */
56743     minColumnWidth : 25,
56744
56745     /**
56746      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
56747      * <b>on initial render.</b> It is more efficient to explicitly size the columns
56748      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
56749      */
56750     autoSizeColumns : false,
56751
56752     /**
56753      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
56754      */
56755     autoSizeHeaders : true,
56756
56757     /**
56758      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
56759      */
56760     monitorWindowResize : true,
56761
56762     /**
56763      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
56764      * rows measured to get a columns size. Default is 0 (all rows).
56765      */
56766     maxRowsToMeasure : 0,
56767
56768     /**
56769      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
56770      */
56771     trackMouseOver : true,
56772
56773     /**
56774     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
56775     */
56776       /**
56777     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
56778     */
56779     
56780     /**
56781     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
56782     */
56783     enableDragDrop : false,
56784     
56785     /**
56786     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
56787     */
56788     enableColumnMove : true,
56789     
56790     /**
56791     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
56792     */
56793     enableColumnHide : true,
56794     
56795     /**
56796     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
56797     */
56798     enableRowHeightSync : false,
56799     
56800     /**
56801     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
56802     */
56803     stripeRows : true,
56804     
56805     /**
56806     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
56807     */
56808     autoHeight : false,
56809
56810     /**
56811      * @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.
56812      */
56813     autoExpandColumn : false,
56814
56815     /**
56816     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
56817     * Default is 50.
56818     */
56819     autoExpandMin : 50,
56820
56821     /**
56822     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
56823     */
56824     autoExpandMax : 1000,
56825
56826     /**
56827     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
56828     */
56829     view : null,
56830
56831     /**
56832     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
56833     */
56834     loadMask : false,
56835     /**
56836     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
56837     */
56838     dropTarget: false,
56839     
56840    
56841     
56842     // private
56843     rendered : false,
56844
56845     /**
56846     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
56847     * of a fixed width. Default is false.
56848     */
56849     /**
56850     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
56851     */
56852     
56853     
56854     /**
56855     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
56856     * %0 is replaced with the number of selected rows.
56857     */
56858     ddText : "{0} selected row{1}",
56859     
56860     
56861     /**
56862      * Called once after all setup has been completed and the grid is ready to be rendered.
56863      * @return {Roo.grid.Grid} this
56864      */
56865     render : function()
56866     {
56867         var c = this.container;
56868         // try to detect autoHeight/width mode
56869         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
56870             this.autoHeight = true;
56871         }
56872         var view = this.getView();
56873         view.init(this);
56874
56875         c.on("click", this.onClick, this);
56876         c.on("dblclick", this.onDblClick, this);
56877         c.on("contextmenu", this.onContextMenu, this);
56878         c.on("keydown", this.onKeyDown, this);
56879         if (Roo.isTouch) {
56880             c.on("touchstart", this.onTouchStart, this);
56881         }
56882
56883         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
56884
56885         this.getSelectionModel().init(this);
56886
56887         view.render();
56888
56889         if(this.loadMask){
56890             this.loadMask = new Roo.LoadMask(this.container,
56891                     Roo.apply({store:this.dataSource}, this.loadMask));
56892         }
56893         
56894         
56895         if (this.toolbar && this.toolbar.xtype) {
56896             this.toolbar.container = this.getView().getHeaderPanel(true);
56897             this.toolbar = new Roo.Toolbar(this.toolbar);
56898         }
56899         if (this.footer && this.footer.xtype) {
56900             this.footer.dataSource = this.getDataSource();
56901             this.footer.container = this.getView().getFooterPanel(true);
56902             this.footer = Roo.factory(this.footer, Roo);
56903         }
56904         if (this.dropTarget && this.dropTarget.xtype) {
56905             delete this.dropTarget.xtype;
56906             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
56907         }
56908         
56909         
56910         this.rendered = true;
56911         this.fireEvent('render', this);
56912         return this;
56913     },
56914
56915     /**
56916      * Reconfigures the grid to use a different Store and Column Model.
56917      * The View will be bound to the new objects and refreshed.
56918      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
56919      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
56920      */
56921     reconfigure : function(dataSource, colModel){
56922         if(this.loadMask){
56923             this.loadMask.destroy();
56924             this.loadMask = new Roo.LoadMask(this.container,
56925                     Roo.apply({store:dataSource}, this.loadMask));
56926         }
56927         this.view.bind(dataSource, colModel);
56928         this.dataSource = dataSource;
56929         this.colModel = colModel;
56930         this.view.refresh(true);
56931     },
56932     /**
56933      * addColumns
56934      * Add's a column, default at the end..
56935      
56936      * @param {int} position to add (default end)
56937      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
56938      */
56939     addColumns : function(pos, ar)
56940     {
56941         
56942         for (var i =0;i< ar.length;i++) {
56943             var cfg = ar[i];
56944             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
56945             this.cm.lookup[cfg.id] = cfg;
56946         }
56947         
56948         
56949         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
56950             pos = this.cm.config.length; //this.cm.config.push(cfg);
56951         } 
56952         pos = Math.max(0,pos);
56953         ar.unshift(0);
56954         ar.unshift(pos);
56955         this.cm.config.splice.apply(this.cm.config, ar);
56956         
56957         
56958         
56959         this.view.generateRules(this.cm);
56960         this.view.refresh(true);
56961         
56962     },
56963     
56964     
56965     
56966     
56967     // private
56968     onKeyDown : function(e){
56969         this.fireEvent("keydown", e);
56970     },
56971
56972     /**
56973      * Destroy this grid.
56974      * @param {Boolean} removeEl True to remove the element
56975      */
56976     destroy : function(removeEl, keepListeners){
56977         if(this.loadMask){
56978             this.loadMask.destroy();
56979         }
56980         var c = this.container;
56981         c.removeAllListeners();
56982         this.view.destroy();
56983         this.colModel.purgeListeners();
56984         if(!keepListeners){
56985             this.purgeListeners();
56986         }
56987         c.update("");
56988         if(removeEl === true){
56989             c.remove();
56990         }
56991     },
56992
56993     // private
56994     processEvent : function(name, e){
56995         // does this fire select???
56996         //Roo.log('grid:processEvent '  + name);
56997         
56998         if (name != 'touchstart' ) {
56999             this.fireEvent(name, e);    
57000         }
57001         
57002         var t = e.getTarget();
57003         var v = this.view;
57004         var header = v.findHeaderIndex(t);
57005         if(header !== false){
57006             var ename = name == 'touchstart' ? 'click' : name;
57007              
57008             this.fireEvent("header" + ename, this, header, e);
57009         }else{
57010             var row = v.findRowIndex(t);
57011             var cell = v.findCellIndex(t);
57012             if (name == 'touchstart') {
57013                 // first touch is always a click.
57014                 // hopefull this happens after selection is updated.?
57015                 name = false;
57016                 
57017                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
57018                     var cs = this.selModel.getSelectedCell();
57019                     if (row == cs[0] && cell == cs[1]){
57020                         name = 'dblclick';
57021                     }
57022                 }
57023                 if (typeof(this.selModel.getSelections) != 'undefined') {
57024                     var cs = this.selModel.getSelections();
57025                     var ds = this.dataSource;
57026                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
57027                         name = 'dblclick';
57028                     }
57029                 }
57030                 if (!name) {
57031                     return;
57032                 }
57033             }
57034             
57035             
57036             if(row !== false){
57037                 this.fireEvent("row" + name, this, row, e);
57038                 if(cell !== false){
57039                     this.fireEvent("cell" + name, this, row, cell, e);
57040                 }
57041             }
57042         }
57043     },
57044
57045     // private
57046     onClick : function(e){
57047         this.processEvent("click", e);
57048     },
57049    // private
57050     onTouchStart : function(e){
57051         this.processEvent("touchstart", e);
57052     },
57053
57054     // private
57055     onContextMenu : function(e, t){
57056         this.processEvent("contextmenu", e);
57057     },
57058
57059     // private
57060     onDblClick : function(e){
57061         this.processEvent("dblclick", e);
57062     },
57063
57064     // private
57065     walkCells : function(row, col, step, fn, scope){
57066         var cm = this.colModel, clen = cm.getColumnCount();
57067         var ds = this.dataSource, rlen = ds.getCount(), first = true;
57068         if(step < 0){
57069             if(col < 0){
57070                 row--;
57071                 first = false;
57072             }
57073             while(row >= 0){
57074                 if(!first){
57075                     col = clen-1;
57076                 }
57077                 first = false;
57078                 while(col >= 0){
57079                     if(fn.call(scope || this, row, col, cm) === true){
57080                         return [row, col];
57081                     }
57082                     col--;
57083                 }
57084                 row--;
57085             }
57086         } else {
57087             if(col >= clen){
57088                 row++;
57089                 first = false;
57090             }
57091             while(row < rlen){
57092                 if(!first){
57093                     col = 0;
57094                 }
57095                 first = false;
57096                 while(col < clen){
57097                     if(fn.call(scope || this, row, col, cm) === true){
57098                         return [row, col];
57099                     }
57100                     col++;
57101                 }
57102                 row++;
57103             }
57104         }
57105         return null;
57106     },
57107
57108     // private
57109     getSelections : function(){
57110         return this.selModel.getSelections();
57111     },
57112
57113     /**
57114      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
57115      * but if manual update is required this method will initiate it.
57116      */
57117     autoSize : function(){
57118         if(this.rendered){
57119             this.view.layout();
57120             if(this.view.adjustForScroll){
57121                 this.view.adjustForScroll();
57122             }
57123         }
57124     },
57125
57126     /**
57127      * Returns the grid's underlying element.
57128      * @return {Element} The element
57129      */
57130     getGridEl : function(){
57131         return this.container;
57132     },
57133
57134     // private for compatibility, overridden by editor grid
57135     stopEditing : function(){},
57136
57137     /**
57138      * Returns the grid's SelectionModel.
57139      * @return {SelectionModel}
57140      */
57141     getSelectionModel : function(){
57142         if(!this.selModel){
57143             this.selModel = new Roo.grid.RowSelectionModel();
57144         }
57145         return this.selModel;
57146     },
57147
57148     /**
57149      * Returns the grid's DataSource.
57150      * @return {DataSource}
57151      */
57152     getDataSource : function(){
57153         return this.dataSource;
57154     },
57155
57156     /**
57157      * Returns the grid's ColumnModel.
57158      * @return {ColumnModel}
57159      */
57160     getColumnModel : function(){
57161         return this.colModel;
57162     },
57163
57164     /**
57165      * Returns the grid's GridView object.
57166      * @return {GridView}
57167      */
57168     getView : function(){
57169         if(!this.view){
57170             this.view = new Roo.grid.GridView(this.viewConfig);
57171             this.relayEvents(this.view, [
57172                 "beforerowremoved", "beforerowsinserted",
57173                 "beforerefresh", "rowremoved",
57174                 "rowsinserted", "rowupdated" ,"refresh"
57175             ]);
57176         }
57177         return this.view;
57178     },
57179     /**
57180      * Called to get grid's drag proxy text, by default returns this.ddText.
57181      * Override this to put something different in the dragged text.
57182      * @return {String}
57183      */
57184     getDragDropText : function(){
57185         var count = this.selModel.getCount();
57186         return String.format(this.ddText, count, count == 1 ? '' : 's');
57187     }
57188 });
57189 /*
57190  * Based on:
57191  * Ext JS Library 1.1.1
57192  * Copyright(c) 2006-2007, Ext JS, LLC.
57193  *
57194  * Originally Released Under LGPL - original licence link has changed is not relivant.
57195  *
57196  * Fork - LGPL
57197  * <script type="text/javascript">
57198  */
57199  /**
57200  * @class Roo.grid.AbstractGridView
57201  * @extends Roo.util.Observable
57202  * @abstract
57203  * Abstract base class for grid Views
57204  * @constructor
57205  */
57206 Roo.grid.AbstractGridView = function(){
57207         this.grid = null;
57208         
57209         this.events = {
57210             "beforerowremoved" : true,
57211             "beforerowsinserted" : true,
57212             "beforerefresh" : true,
57213             "rowremoved" : true,
57214             "rowsinserted" : true,
57215             "rowupdated" : true,
57216             "refresh" : true
57217         };
57218     Roo.grid.AbstractGridView.superclass.constructor.call(this);
57219 };
57220
57221 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
57222     rowClass : "x-grid-row",
57223     cellClass : "x-grid-cell",
57224     tdClass : "x-grid-td",
57225     hdClass : "x-grid-hd",
57226     splitClass : "x-grid-hd-split",
57227     
57228     init: function(grid){
57229         this.grid = grid;
57230                 var cid = this.grid.getGridEl().id;
57231         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
57232         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
57233         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
57234         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
57235         },
57236         
57237     getColumnRenderers : function(){
57238         var renderers = [];
57239         var cm = this.grid.colModel;
57240         var colCount = cm.getColumnCount();
57241         for(var i = 0; i < colCount; i++){
57242             renderers[i] = cm.getRenderer(i);
57243         }
57244         return renderers;
57245     },
57246     
57247     getColumnIds : function(){
57248         var ids = [];
57249         var cm = this.grid.colModel;
57250         var colCount = cm.getColumnCount();
57251         for(var i = 0; i < colCount; i++){
57252             ids[i] = cm.getColumnId(i);
57253         }
57254         return ids;
57255     },
57256     
57257     getDataIndexes : function(){
57258         if(!this.indexMap){
57259             this.indexMap = this.buildIndexMap();
57260         }
57261         return this.indexMap.colToData;
57262     },
57263     
57264     getColumnIndexByDataIndex : function(dataIndex){
57265         if(!this.indexMap){
57266             this.indexMap = this.buildIndexMap();
57267         }
57268         return this.indexMap.dataToCol[dataIndex];
57269     },
57270     
57271     /**
57272      * Set a css style for a column dynamically. 
57273      * @param {Number} colIndex The index of the column
57274      * @param {String} name The css property name
57275      * @param {String} value The css value
57276      */
57277     setCSSStyle : function(colIndex, name, value){
57278         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
57279         Roo.util.CSS.updateRule(selector, name, value);
57280     },
57281     
57282     generateRules : function(cm){
57283         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
57284         Roo.util.CSS.removeStyleSheet(rulesId);
57285         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57286             var cid = cm.getColumnId(i);
57287             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
57288                          this.tdSelector, cid, " {\n}\n",
57289                          this.hdSelector, cid, " {\n}\n",
57290                          this.splitSelector, cid, " {\n}\n");
57291         }
57292         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57293     }
57294 });/*
57295  * Based on:
57296  * Ext JS Library 1.1.1
57297  * Copyright(c) 2006-2007, Ext JS, LLC.
57298  *
57299  * Originally Released Under LGPL - original licence link has changed is not relivant.
57300  *
57301  * Fork - LGPL
57302  * <script type="text/javascript">
57303  */
57304
57305 // private
57306 // This is a support class used internally by the Grid components
57307 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
57308     this.grid = grid;
57309     this.view = grid.getView();
57310     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57311     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
57312     if(hd2){
57313         this.setHandleElId(Roo.id(hd));
57314         this.setOuterHandleElId(Roo.id(hd2));
57315     }
57316     this.scroll = false;
57317 };
57318 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
57319     maxDragWidth: 120,
57320     getDragData : function(e){
57321         var t = Roo.lib.Event.getTarget(e);
57322         var h = this.view.findHeaderCell(t);
57323         if(h){
57324             return {ddel: h.firstChild, header:h};
57325         }
57326         return false;
57327     },
57328
57329     onInitDrag : function(e){
57330         this.view.headersDisabled = true;
57331         var clone = this.dragData.ddel.cloneNode(true);
57332         clone.id = Roo.id();
57333         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
57334         this.proxy.update(clone);
57335         return true;
57336     },
57337
57338     afterValidDrop : function(){
57339         var v = this.view;
57340         setTimeout(function(){
57341             v.headersDisabled = false;
57342         }, 50);
57343     },
57344
57345     afterInvalidDrop : function(){
57346         var v = this.view;
57347         setTimeout(function(){
57348             v.headersDisabled = false;
57349         }, 50);
57350     }
57351 });
57352 /*
57353  * Based on:
57354  * Ext JS Library 1.1.1
57355  * Copyright(c) 2006-2007, Ext JS, LLC.
57356  *
57357  * Originally Released Under LGPL - original licence link has changed is not relivant.
57358  *
57359  * Fork - LGPL
57360  * <script type="text/javascript">
57361  */
57362 // private
57363 // This is a support class used internally by the Grid components
57364 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57365     this.grid = grid;
57366     this.view = grid.getView();
57367     // split the proxies so they don't interfere with mouse events
57368     this.proxyTop = Roo.DomHelper.append(document.body, {
57369         cls:"col-move-top", html:"&#160;"
57370     }, true);
57371     this.proxyBottom = Roo.DomHelper.append(document.body, {
57372         cls:"col-move-bottom", html:"&#160;"
57373     }, true);
57374     this.proxyTop.hide = this.proxyBottom.hide = function(){
57375         this.setLeftTop(-100,-100);
57376         this.setStyle("visibility", "hidden");
57377     };
57378     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57379     // temporarily disabled
57380     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57381     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57382 };
57383 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57384     proxyOffsets : [-4, -9],
57385     fly: Roo.Element.fly,
57386
57387     getTargetFromEvent : function(e){
57388         var t = Roo.lib.Event.getTarget(e);
57389         var cindex = this.view.findCellIndex(t);
57390         if(cindex !== false){
57391             return this.view.getHeaderCell(cindex);
57392         }
57393         return null;
57394     },
57395
57396     nextVisible : function(h){
57397         var v = this.view, cm = this.grid.colModel;
57398         h = h.nextSibling;
57399         while(h){
57400             if(!cm.isHidden(v.getCellIndex(h))){
57401                 return h;
57402             }
57403             h = h.nextSibling;
57404         }
57405         return null;
57406     },
57407
57408     prevVisible : function(h){
57409         var v = this.view, cm = this.grid.colModel;
57410         h = h.prevSibling;
57411         while(h){
57412             if(!cm.isHidden(v.getCellIndex(h))){
57413                 return h;
57414             }
57415             h = h.prevSibling;
57416         }
57417         return null;
57418     },
57419
57420     positionIndicator : function(h, n, e){
57421         var x = Roo.lib.Event.getPageX(e);
57422         var r = Roo.lib.Dom.getRegion(n.firstChild);
57423         var px, pt, py = r.top + this.proxyOffsets[1];
57424         if((r.right - x) <= (r.right-r.left)/2){
57425             px = r.right+this.view.borderWidth;
57426             pt = "after";
57427         }else{
57428             px = r.left;
57429             pt = "before";
57430         }
57431         var oldIndex = this.view.getCellIndex(h);
57432         var newIndex = this.view.getCellIndex(n);
57433
57434         if(this.grid.colModel.isFixed(newIndex)){
57435             return false;
57436         }
57437
57438         var locked = this.grid.colModel.isLocked(newIndex);
57439
57440         if(pt == "after"){
57441             newIndex++;
57442         }
57443         if(oldIndex < newIndex){
57444             newIndex--;
57445         }
57446         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
57447             return false;
57448         }
57449         px +=  this.proxyOffsets[0];
57450         this.proxyTop.setLeftTop(px, py);
57451         this.proxyTop.show();
57452         if(!this.bottomOffset){
57453             this.bottomOffset = this.view.mainHd.getHeight();
57454         }
57455         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
57456         this.proxyBottom.show();
57457         return pt;
57458     },
57459
57460     onNodeEnter : function(n, dd, e, data){
57461         if(data.header != n){
57462             this.positionIndicator(data.header, n, e);
57463         }
57464     },
57465
57466     onNodeOver : function(n, dd, e, data){
57467         var result = false;
57468         if(data.header != n){
57469             result = this.positionIndicator(data.header, n, e);
57470         }
57471         if(!result){
57472             this.proxyTop.hide();
57473             this.proxyBottom.hide();
57474         }
57475         return result ? this.dropAllowed : this.dropNotAllowed;
57476     },
57477
57478     onNodeOut : function(n, dd, e, data){
57479         this.proxyTop.hide();
57480         this.proxyBottom.hide();
57481     },
57482
57483     onNodeDrop : function(n, dd, e, data){
57484         var h = data.header;
57485         if(h != n){
57486             var cm = this.grid.colModel;
57487             var x = Roo.lib.Event.getPageX(e);
57488             var r = Roo.lib.Dom.getRegion(n.firstChild);
57489             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
57490             var oldIndex = this.view.getCellIndex(h);
57491             var newIndex = this.view.getCellIndex(n);
57492             var locked = cm.isLocked(newIndex);
57493             if(pt == "after"){
57494                 newIndex++;
57495             }
57496             if(oldIndex < newIndex){
57497                 newIndex--;
57498             }
57499             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
57500                 return false;
57501             }
57502             cm.setLocked(oldIndex, locked, true);
57503             cm.moveColumn(oldIndex, newIndex);
57504             this.grid.fireEvent("columnmove", oldIndex, newIndex);
57505             return true;
57506         }
57507         return false;
57508     }
57509 });
57510 /*
57511  * Based on:
57512  * Ext JS Library 1.1.1
57513  * Copyright(c) 2006-2007, Ext JS, LLC.
57514  *
57515  * Originally Released Under LGPL - original licence link has changed is not relivant.
57516  *
57517  * Fork - LGPL
57518  * <script type="text/javascript">
57519  */
57520   
57521 /**
57522  * @class Roo.grid.GridView
57523  * @extends Roo.util.Observable
57524  *
57525  * @constructor
57526  * @param {Object} config
57527  */
57528 Roo.grid.GridView = function(config){
57529     Roo.grid.GridView.superclass.constructor.call(this);
57530     this.el = null;
57531
57532     Roo.apply(this, config);
57533 };
57534
57535 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
57536
57537     unselectable :  'unselectable="on"',
57538     unselectableCls :  'x-unselectable',
57539     
57540     
57541     rowClass : "x-grid-row",
57542
57543     cellClass : "x-grid-col",
57544
57545     tdClass : "x-grid-td",
57546
57547     hdClass : "x-grid-hd",
57548
57549     splitClass : "x-grid-split",
57550
57551     sortClasses : ["sort-asc", "sort-desc"],
57552
57553     enableMoveAnim : false,
57554
57555     hlColor: "C3DAF9",
57556
57557     dh : Roo.DomHelper,
57558
57559     fly : Roo.Element.fly,
57560
57561     css : Roo.util.CSS,
57562
57563     borderWidth: 1,
57564
57565     splitOffset: 3,
57566
57567     scrollIncrement : 22,
57568
57569     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
57570
57571     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
57572
57573     bind : function(ds, cm){
57574         if(this.ds){
57575             this.ds.un("load", this.onLoad, this);
57576             this.ds.un("datachanged", this.onDataChange, this);
57577             this.ds.un("add", this.onAdd, this);
57578             this.ds.un("remove", this.onRemove, this);
57579             this.ds.un("update", this.onUpdate, this);
57580             this.ds.un("clear", this.onClear, this);
57581         }
57582         if(ds){
57583             ds.on("load", this.onLoad, this);
57584             ds.on("datachanged", this.onDataChange, this);
57585             ds.on("add", this.onAdd, this);
57586             ds.on("remove", this.onRemove, this);
57587             ds.on("update", this.onUpdate, this);
57588             ds.on("clear", this.onClear, this);
57589         }
57590         this.ds = ds;
57591
57592         if(this.cm){
57593             this.cm.un("widthchange", this.onColWidthChange, this);
57594             this.cm.un("headerchange", this.onHeaderChange, this);
57595             this.cm.un("hiddenchange", this.onHiddenChange, this);
57596             this.cm.un("columnmoved", this.onColumnMove, this);
57597             this.cm.un("columnlockchange", this.onColumnLock, this);
57598         }
57599         if(cm){
57600             this.generateRules(cm);
57601             cm.on("widthchange", this.onColWidthChange, this);
57602             cm.on("headerchange", this.onHeaderChange, this);
57603             cm.on("hiddenchange", this.onHiddenChange, this);
57604             cm.on("columnmoved", this.onColumnMove, this);
57605             cm.on("columnlockchange", this.onColumnLock, this);
57606         }
57607         this.cm = cm;
57608     },
57609
57610     init: function(grid){
57611         Roo.grid.GridView.superclass.init.call(this, grid);
57612
57613         this.bind(grid.dataSource, grid.colModel);
57614
57615         grid.on("headerclick", this.handleHeaderClick, this);
57616
57617         if(grid.trackMouseOver){
57618             grid.on("mouseover", this.onRowOver, this);
57619             grid.on("mouseout", this.onRowOut, this);
57620         }
57621         grid.cancelTextSelection = function(){};
57622         this.gridId = grid.id;
57623
57624         var tpls = this.templates || {};
57625
57626         if(!tpls.master){
57627             tpls.master = new Roo.Template(
57628                '<div class="x-grid" hidefocus="true">',
57629                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
57630                   '<div class="x-grid-topbar"></div>',
57631                   '<div class="x-grid-scroller"><div></div></div>',
57632                   '<div class="x-grid-locked">',
57633                       '<div class="x-grid-header">{lockedHeader}</div>',
57634                       '<div class="x-grid-body">{lockedBody}</div>',
57635                   "</div>",
57636                   '<div class="x-grid-viewport">',
57637                       '<div class="x-grid-header">{header}</div>',
57638                       '<div class="x-grid-body">{body}</div>',
57639                   "</div>",
57640                   '<div class="x-grid-bottombar"></div>',
57641                  
57642                   '<div class="x-grid-resize-proxy">&#160;</div>',
57643                "</div>"
57644             );
57645             tpls.master.disableformats = true;
57646         }
57647
57648         if(!tpls.header){
57649             tpls.header = new Roo.Template(
57650                '<table border="0" cellspacing="0" cellpadding="0">',
57651                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
57652                "</table>{splits}"
57653             );
57654             tpls.header.disableformats = true;
57655         }
57656         tpls.header.compile();
57657
57658         if(!tpls.hcell){
57659             tpls.hcell = new Roo.Template(
57660                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
57661                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
57662                 "</div></td>"
57663              );
57664              tpls.hcell.disableFormats = true;
57665         }
57666         tpls.hcell.compile();
57667
57668         if(!tpls.hsplit){
57669             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
57670                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
57671             tpls.hsplit.disableFormats = true;
57672         }
57673         tpls.hsplit.compile();
57674
57675         if(!tpls.body){
57676             tpls.body = new Roo.Template(
57677                '<table border="0" cellspacing="0" cellpadding="0">',
57678                "<tbody>{rows}</tbody>",
57679                "</table>"
57680             );
57681             tpls.body.disableFormats = true;
57682         }
57683         tpls.body.compile();
57684
57685         if(!tpls.row){
57686             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
57687             tpls.row.disableFormats = true;
57688         }
57689         tpls.row.compile();
57690
57691         if(!tpls.cell){
57692             tpls.cell = new Roo.Template(
57693                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
57694                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
57695                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
57696                 "</td>"
57697             );
57698             tpls.cell.disableFormats = true;
57699         }
57700         tpls.cell.compile();
57701
57702         this.templates = tpls;
57703     },
57704
57705     // remap these for backwards compat
57706     onColWidthChange : function(){
57707         this.updateColumns.apply(this, arguments);
57708     },
57709     onHeaderChange : function(){
57710         this.updateHeaders.apply(this, arguments);
57711     }, 
57712     onHiddenChange : function(){
57713         this.handleHiddenChange.apply(this, arguments);
57714     },
57715     onColumnMove : function(){
57716         this.handleColumnMove.apply(this, arguments);
57717     },
57718     onColumnLock : function(){
57719         this.handleLockChange.apply(this, arguments);
57720     },
57721
57722     onDataChange : function(){
57723         this.refresh();
57724         this.updateHeaderSortState();
57725     },
57726
57727     onClear : function(){
57728         this.refresh();
57729     },
57730
57731     onUpdate : function(ds, record){
57732         this.refreshRow(record);
57733     },
57734
57735     refreshRow : function(record){
57736         var ds = this.ds, index;
57737         if(typeof record == 'number'){
57738             index = record;
57739             record = ds.getAt(index);
57740         }else{
57741             index = ds.indexOf(record);
57742         }
57743         this.insertRows(ds, index, index, true);
57744         this.onRemove(ds, record, index+1, true);
57745         this.syncRowHeights(index, index);
57746         this.layout();
57747         this.fireEvent("rowupdated", this, index, record);
57748     },
57749
57750     onAdd : function(ds, records, index){
57751         this.insertRows(ds, index, index + (records.length-1));
57752     },
57753
57754     onRemove : function(ds, record, index, isUpdate){
57755         if(isUpdate !== true){
57756             this.fireEvent("beforerowremoved", this, index, record);
57757         }
57758         var bt = this.getBodyTable(), lt = this.getLockedTable();
57759         if(bt.rows[index]){
57760             bt.firstChild.removeChild(bt.rows[index]);
57761         }
57762         if(lt.rows[index]){
57763             lt.firstChild.removeChild(lt.rows[index]);
57764         }
57765         if(isUpdate !== true){
57766             this.stripeRows(index);
57767             this.syncRowHeights(index, index);
57768             this.layout();
57769             this.fireEvent("rowremoved", this, index, record);
57770         }
57771     },
57772
57773     onLoad : function(){
57774         this.scrollToTop();
57775     },
57776
57777     /**
57778      * Scrolls the grid to the top
57779      */
57780     scrollToTop : function(){
57781         if(this.scroller){
57782             this.scroller.dom.scrollTop = 0;
57783             this.syncScroll();
57784         }
57785     },
57786
57787     /**
57788      * Gets a panel in the header of the grid that can be used for toolbars etc.
57789      * After modifying the contents of this panel a call to grid.autoSize() may be
57790      * required to register any changes in size.
57791      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
57792      * @return Roo.Element
57793      */
57794     getHeaderPanel : function(doShow){
57795         if(doShow){
57796             this.headerPanel.show();
57797         }
57798         return this.headerPanel;
57799     },
57800
57801     /**
57802      * Gets a panel in the footer of the grid that can be used for toolbars etc.
57803      * After modifying the contents of this panel a call to grid.autoSize() may be
57804      * required to register any changes in size.
57805      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
57806      * @return Roo.Element
57807      */
57808     getFooterPanel : function(doShow){
57809         if(doShow){
57810             this.footerPanel.show();
57811         }
57812         return this.footerPanel;
57813     },
57814
57815     initElements : function(){
57816         var E = Roo.Element;
57817         var el = this.grid.getGridEl().dom.firstChild;
57818         var cs = el.childNodes;
57819
57820         this.el = new E(el);
57821         
57822          this.focusEl = new E(el.firstChild);
57823         this.focusEl.swallowEvent("click", true);
57824         
57825         this.headerPanel = new E(cs[1]);
57826         this.headerPanel.enableDisplayMode("block");
57827
57828         this.scroller = new E(cs[2]);
57829         this.scrollSizer = new E(this.scroller.dom.firstChild);
57830
57831         this.lockedWrap = new E(cs[3]);
57832         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
57833         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
57834
57835         this.mainWrap = new E(cs[4]);
57836         this.mainHd = new E(this.mainWrap.dom.firstChild);
57837         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
57838
57839         this.footerPanel = new E(cs[5]);
57840         this.footerPanel.enableDisplayMode("block");
57841
57842         this.resizeProxy = new E(cs[6]);
57843
57844         this.headerSelector = String.format(
57845            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
57846            this.lockedHd.id, this.mainHd.id
57847         );
57848
57849         this.splitterSelector = String.format(
57850            '#{0} div.x-grid-split, #{1} div.x-grid-split',
57851            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
57852         );
57853     },
57854     idToCssName : function(s)
57855     {
57856         return s.replace(/[^a-z0-9]+/ig, '-');
57857     },
57858
57859     getHeaderCell : function(index){
57860         return Roo.DomQuery.select(this.headerSelector)[index];
57861     },
57862
57863     getHeaderCellMeasure : function(index){
57864         return this.getHeaderCell(index).firstChild;
57865     },
57866
57867     getHeaderCellText : function(index){
57868         return this.getHeaderCell(index).firstChild.firstChild;
57869     },
57870
57871     getLockedTable : function(){
57872         return this.lockedBody.dom.firstChild;
57873     },
57874
57875     getBodyTable : function(){
57876         return this.mainBody.dom.firstChild;
57877     },
57878
57879     getLockedRow : function(index){
57880         return this.getLockedTable().rows[index];
57881     },
57882
57883     getRow : function(index){
57884         return this.getBodyTable().rows[index];
57885     },
57886
57887     getRowComposite : function(index){
57888         if(!this.rowEl){
57889             this.rowEl = new Roo.CompositeElementLite();
57890         }
57891         var els = [], lrow, mrow;
57892         if(lrow = this.getLockedRow(index)){
57893             els.push(lrow);
57894         }
57895         if(mrow = this.getRow(index)){
57896             els.push(mrow);
57897         }
57898         this.rowEl.elements = els;
57899         return this.rowEl;
57900     },
57901     /**
57902      * Gets the 'td' of the cell
57903      * 
57904      * @param {Integer} rowIndex row to select
57905      * @param {Integer} colIndex column to select
57906      * 
57907      * @return {Object} 
57908      */
57909     getCell : function(rowIndex, colIndex){
57910         var locked = this.cm.getLockedCount();
57911         var source;
57912         if(colIndex < locked){
57913             source = this.lockedBody.dom.firstChild;
57914         }else{
57915             source = this.mainBody.dom.firstChild;
57916             colIndex -= locked;
57917         }
57918         return source.rows[rowIndex].childNodes[colIndex];
57919     },
57920
57921     getCellText : function(rowIndex, colIndex){
57922         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
57923     },
57924
57925     getCellBox : function(cell){
57926         var b = this.fly(cell).getBox();
57927         if(Roo.isOpera){ // opera fails to report the Y
57928             b.y = cell.offsetTop + this.mainBody.getY();
57929         }
57930         return b;
57931     },
57932
57933     getCellIndex : function(cell){
57934         var id = String(cell.className).match(this.cellRE);
57935         if(id){
57936             return parseInt(id[1], 10);
57937         }
57938         return 0;
57939     },
57940
57941     findHeaderIndex : function(n){
57942         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57943         return r ? this.getCellIndex(r) : false;
57944     },
57945
57946     findHeaderCell : function(n){
57947         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57948         return r ? r : false;
57949     },
57950
57951     findRowIndex : function(n){
57952         if(!n){
57953             return false;
57954         }
57955         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
57956         return r ? r.rowIndex : false;
57957     },
57958
57959     findCellIndex : function(node){
57960         var stop = this.el.dom;
57961         while(node && node != stop){
57962             if(this.findRE.test(node.className)){
57963                 return this.getCellIndex(node);
57964             }
57965             node = node.parentNode;
57966         }
57967         return false;
57968     },
57969
57970     getColumnId : function(index){
57971         return this.cm.getColumnId(index);
57972     },
57973
57974     getSplitters : function()
57975     {
57976         if(this.splitterSelector){
57977            return Roo.DomQuery.select(this.splitterSelector);
57978         }else{
57979             return null;
57980       }
57981     },
57982
57983     getSplitter : function(index){
57984         return this.getSplitters()[index];
57985     },
57986
57987     onRowOver : function(e, t){
57988         var row;
57989         if((row = this.findRowIndex(t)) !== false){
57990             this.getRowComposite(row).addClass("x-grid-row-over");
57991         }
57992     },
57993
57994     onRowOut : function(e, t){
57995         var row;
57996         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
57997             this.getRowComposite(row).removeClass("x-grid-row-over");
57998         }
57999     },
58000
58001     renderHeaders : function(){
58002         var cm = this.cm;
58003         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
58004         var cb = [], lb = [], sb = [], lsb = [], p = {};
58005         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58006             p.cellId = "x-grid-hd-0-" + i;
58007             p.splitId = "x-grid-csplit-0-" + i;
58008             p.id = cm.getColumnId(i);
58009             p.value = cm.getColumnHeader(i) || "";
58010             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
58011             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
58012             if(!cm.isLocked(i)){
58013                 cb[cb.length] = ct.apply(p);
58014                 sb[sb.length] = st.apply(p);
58015             }else{
58016                 lb[lb.length] = ct.apply(p);
58017                 lsb[lsb.length] = st.apply(p);
58018             }
58019         }
58020         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
58021                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
58022     },
58023
58024     updateHeaders : function(){
58025         var html = this.renderHeaders();
58026         this.lockedHd.update(html[0]);
58027         this.mainHd.update(html[1]);
58028     },
58029
58030     /**
58031      * Focuses the specified row.
58032      * @param {Number} row The row index
58033      */
58034     focusRow : function(row)
58035     {
58036         //Roo.log('GridView.focusRow');
58037         var x = this.scroller.dom.scrollLeft;
58038         this.focusCell(row, 0, false);
58039         this.scroller.dom.scrollLeft = x;
58040     },
58041
58042     /**
58043      * Focuses the specified cell.
58044      * @param {Number} row The row index
58045      * @param {Number} col The column index
58046      * @param {Boolean} hscroll false to disable horizontal scrolling
58047      */
58048     focusCell : function(row, col, hscroll)
58049     {
58050         //Roo.log('GridView.focusCell');
58051         var el = this.ensureVisible(row, col, hscroll);
58052         this.focusEl.alignTo(el, "tl-tl");
58053         if(Roo.isGecko){
58054             this.focusEl.focus();
58055         }else{
58056             this.focusEl.focus.defer(1, this.focusEl);
58057         }
58058     },
58059
58060     /**
58061      * Scrolls the specified cell into view
58062      * @param {Number} row The row index
58063      * @param {Number} col The column index
58064      * @param {Boolean} hscroll false to disable horizontal scrolling
58065      */
58066     ensureVisible : function(row, col, hscroll)
58067     {
58068         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
58069         //return null; //disable for testing.
58070         if(typeof row != "number"){
58071             row = row.rowIndex;
58072         }
58073         if(row < 0 && row >= this.ds.getCount()){
58074             return  null;
58075         }
58076         col = (col !== undefined ? col : 0);
58077         var cm = this.grid.colModel;
58078         while(cm.isHidden(col)){
58079             col++;
58080         }
58081
58082         var el = this.getCell(row, col);
58083         if(!el){
58084             return null;
58085         }
58086         var c = this.scroller.dom;
58087
58088         var ctop = parseInt(el.offsetTop, 10);
58089         var cleft = parseInt(el.offsetLeft, 10);
58090         var cbot = ctop + el.offsetHeight;
58091         var cright = cleft + el.offsetWidth;
58092         
58093         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
58094         var stop = parseInt(c.scrollTop, 10);
58095         var sleft = parseInt(c.scrollLeft, 10);
58096         var sbot = stop + ch;
58097         var sright = sleft + c.clientWidth;
58098         /*
58099         Roo.log('GridView.ensureVisible:' +
58100                 ' ctop:' + ctop +
58101                 ' c.clientHeight:' + c.clientHeight +
58102                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
58103                 ' stop:' + stop +
58104                 ' cbot:' + cbot +
58105                 ' sbot:' + sbot +
58106                 ' ch:' + ch  
58107                 );
58108         */
58109         if(ctop < stop){
58110             c.scrollTop = ctop;
58111             //Roo.log("set scrolltop to ctop DISABLE?");
58112         }else if(cbot > sbot){
58113             //Roo.log("set scrolltop to cbot-ch");
58114             c.scrollTop = cbot-ch;
58115         }
58116         
58117         if(hscroll !== false){
58118             if(cleft < sleft){
58119                 c.scrollLeft = cleft;
58120             }else if(cright > sright){
58121                 c.scrollLeft = cright-c.clientWidth;
58122             }
58123         }
58124          
58125         return el;
58126     },
58127
58128     updateColumns : function(){
58129         this.grid.stopEditing();
58130         var cm = this.grid.colModel, colIds = this.getColumnIds();
58131         //var totalWidth = cm.getTotalWidth();
58132         var pos = 0;
58133         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58134             //if(cm.isHidden(i)) continue;
58135             var w = cm.getColumnWidth(i);
58136             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58137             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58138         }
58139         this.updateSplitters();
58140     },
58141
58142     generateRules : function(cm){
58143         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
58144         Roo.util.CSS.removeStyleSheet(rulesId);
58145         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58146             var cid = cm.getColumnId(i);
58147             var align = '';
58148             if(cm.config[i].align){
58149                 align = 'text-align:'+cm.config[i].align+';';
58150             }
58151             var hidden = '';
58152             if(cm.isHidden(i)){
58153                 hidden = 'display:none;';
58154             }
58155             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
58156             ruleBuf.push(
58157                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
58158                     this.hdSelector, cid, " {\n", align, width, "}\n",
58159                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
58160                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
58161         }
58162         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58163     },
58164
58165     updateSplitters : function(){
58166         var cm = this.cm, s = this.getSplitters();
58167         if(s){ // splitters not created yet
58168             var pos = 0, locked = true;
58169             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58170                 if(cm.isHidden(i)) {
58171                     continue;
58172                 }
58173                 var w = cm.getColumnWidth(i); // make sure it's a number
58174                 if(!cm.isLocked(i) && locked){
58175                     pos = 0;
58176                     locked = false;
58177                 }
58178                 pos += w;
58179                 s[i].style.left = (pos-this.splitOffset) + "px";
58180             }
58181         }
58182     },
58183
58184     handleHiddenChange : function(colModel, colIndex, hidden){
58185         if(hidden){
58186             this.hideColumn(colIndex);
58187         }else{
58188             this.unhideColumn(colIndex);
58189         }
58190     },
58191
58192     hideColumn : function(colIndex){
58193         var cid = this.getColumnId(colIndex);
58194         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
58195         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
58196         if(Roo.isSafari){
58197             this.updateHeaders();
58198         }
58199         this.updateSplitters();
58200         this.layout();
58201     },
58202
58203     unhideColumn : function(colIndex){
58204         var cid = this.getColumnId(colIndex);
58205         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
58206         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
58207
58208         if(Roo.isSafari){
58209             this.updateHeaders();
58210         }
58211         this.updateSplitters();
58212         this.layout();
58213     },
58214
58215     insertRows : function(dm, firstRow, lastRow, isUpdate){
58216         if(firstRow == 0 && lastRow == dm.getCount()-1){
58217             this.refresh();
58218         }else{
58219             if(!isUpdate){
58220                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
58221             }
58222             var s = this.getScrollState();
58223             var markup = this.renderRows(firstRow, lastRow);
58224             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
58225             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
58226             this.restoreScroll(s);
58227             if(!isUpdate){
58228                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
58229                 this.syncRowHeights(firstRow, lastRow);
58230                 this.stripeRows(firstRow);
58231                 this.layout();
58232             }
58233         }
58234     },
58235
58236     bufferRows : function(markup, target, index){
58237         var before = null, trows = target.rows, tbody = target.tBodies[0];
58238         if(index < trows.length){
58239             before = trows[index];
58240         }
58241         var b = document.createElement("div");
58242         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
58243         var rows = b.firstChild.rows;
58244         for(var i = 0, len = rows.length; i < len; i++){
58245             if(before){
58246                 tbody.insertBefore(rows[0], before);
58247             }else{
58248                 tbody.appendChild(rows[0]);
58249             }
58250         }
58251         b.innerHTML = "";
58252         b = null;
58253     },
58254
58255     deleteRows : function(dm, firstRow, lastRow){
58256         if(dm.getRowCount()<1){
58257             this.fireEvent("beforerefresh", this);
58258             this.mainBody.update("");
58259             this.lockedBody.update("");
58260             this.fireEvent("refresh", this);
58261         }else{
58262             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
58263             var bt = this.getBodyTable();
58264             var tbody = bt.firstChild;
58265             var rows = bt.rows;
58266             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
58267                 tbody.removeChild(rows[firstRow]);
58268             }
58269             this.stripeRows(firstRow);
58270             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
58271         }
58272     },
58273
58274     updateRows : function(dataSource, firstRow, lastRow){
58275         var s = this.getScrollState();
58276         this.refresh();
58277         this.restoreScroll(s);
58278     },
58279
58280     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
58281         if(!noRefresh){
58282            this.refresh();
58283         }
58284         this.updateHeaderSortState();
58285     },
58286
58287     getScrollState : function(){
58288         
58289         var sb = this.scroller.dom;
58290         return {left: sb.scrollLeft, top: sb.scrollTop};
58291     },
58292
58293     stripeRows : function(startRow){
58294         if(!this.grid.stripeRows || this.ds.getCount() < 1){
58295             return;
58296         }
58297         startRow = startRow || 0;
58298         var rows = this.getBodyTable().rows;
58299         var lrows = this.getLockedTable().rows;
58300         var cls = ' x-grid-row-alt ';
58301         for(var i = startRow, len = rows.length; i < len; i++){
58302             var row = rows[i], lrow = lrows[i];
58303             var isAlt = ((i+1) % 2 == 0);
58304             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
58305             if(isAlt == hasAlt){
58306                 continue;
58307             }
58308             if(isAlt){
58309                 row.className += " x-grid-row-alt";
58310             }else{
58311                 row.className = row.className.replace("x-grid-row-alt", "");
58312             }
58313             if(lrow){
58314                 lrow.className = row.className;
58315             }
58316         }
58317     },
58318
58319     restoreScroll : function(state){
58320         //Roo.log('GridView.restoreScroll');
58321         var sb = this.scroller.dom;
58322         sb.scrollLeft = state.left;
58323         sb.scrollTop = state.top;
58324         this.syncScroll();
58325     },
58326
58327     syncScroll : function(){
58328         //Roo.log('GridView.syncScroll');
58329         var sb = this.scroller.dom;
58330         var sh = this.mainHd.dom;
58331         var bs = this.mainBody.dom;
58332         var lv = this.lockedBody.dom;
58333         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
58334         lv.scrollTop = bs.scrollTop = sb.scrollTop;
58335     },
58336
58337     handleScroll : function(e){
58338         this.syncScroll();
58339         var sb = this.scroller.dom;
58340         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
58341         e.stopEvent();
58342     },
58343
58344     handleWheel : function(e){
58345         var d = e.getWheelDelta();
58346         this.scroller.dom.scrollTop -= d*22;
58347         // set this here to prevent jumpy scrolling on large tables
58348         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
58349         e.stopEvent();
58350     },
58351
58352     renderRows : function(startRow, endRow){
58353         // pull in all the crap needed to render rows
58354         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
58355         var colCount = cm.getColumnCount();
58356
58357         if(ds.getCount() < 1){
58358             return ["", ""];
58359         }
58360
58361         // build a map for all the columns
58362         var cs = [];
58363         for(var i = 0; i < colCount; i++){
58364             var name = cm.getDataIndex(i);
58365             cs[i] = {
58366                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58367                 renderer : cm.getRenderer(i),
58368                 id : cm.getColumnId(i),
58369                 locked : cm.isLocked(i),
58370                 has_editor : cm.isCellEditable(i)
58371             };
58372         }
58373
58374         startRow = startRow || 0;
58375         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58376
58377         // records to render
58378         var rs = ds.getRange(startRow, endRow);
58379
58380         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58381     },
58382
58383     // As much as I hate to duplicate code, this was branched because FireFox really hates
58384     // [].join("") on strings. The performance difference was substantial enough to
58385     // branch this function
58386     doRender : Roo.isGecko ?
58387             function(cs, rs, ds, startRow, colCount, stripe){
58388                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58389                 // buffers
58390                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58391                 
58392                 var hasListener = this.grid.hasListener('rowclass');
58393                 var rowcfg = {};
58394                 for(var j = 0, len = rs.length; j < len; j++){
58395                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58396                     for(var i = 0; i < colCount; i++){
58397                         c = cs[i];
58398                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58399                         p.id = c.id;
58400                         p.css = p.attr = "";
58401                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58402                         if(p.value == undefined || p.value === "") {
58403                             p.value = "&#160;";
58404                         }
58405                         if(c.has_editor){
58406                             p.css += ' x-grid-editable-cell';
58407                         }
58408                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58409                             p.css +=  ' x-grid-dirty-cell';
58410                         }
58411                         var markup = ct.apply(p);
58412                         if(!c.locked){
58413                             cb+= markup;
58414                         }else{
58415                             lcb+= markup;
58416                         }
58417                     }
58418                     var alt = [];
58419                     if(stripe && ((rowIndex+1) % 2 == 0)){
58420                         alt.push("x-grid-row-alt")
58421                     }
58422                     if(r.dirty){
58423                         alt.push(  " x-grid-dirty-row");
58424                     }
58425                     rp.cells = lcb;
58426                     if(this.getRowClass){
58427                         alt.push(this.getRowClass(r, rowIndex));
58428                     }
58429                     if (hasListener) {
58430                         rowcfg = {
58431                              
58432                             record: r,
58433                             rowIndex : rowIndex,
58434                             rowClass : ''
58435                         };
58436                         this.grid.fireEvent('rowclass', this, rowcfg);
58437                         alt.push(rowcfg.rowClass);
58438                     }
58439                     rp.alt = alt.join(" ");
58440                     lbuf+= rt.apply(rp);
58441                     rp.cells = cb;
58442                     buf+=  rt.apply(rp);
58443                 }
58444                 return [lbuf, buf];
58445             } :
58446             function(cs, rs, ds, startRow, colCount, stripe){
58447                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58448                 // buffers
58449                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58450                 var hasListener = this.grid.hasListener('rowclass');
58451  
58452                 var rowcfg = {};
58453                 for(var j = 0, len = rs.length; j < len; j++){
58454                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
58455                     for(var i = 0; i < colCount; i++){
58456                         c = cs[i];
58457                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58458                         p.id = c.id;
58459                         p.css = p.attr = "";
58460                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58461                         if(p.value == undefined || p.value === "") {
58462                             p.value = "&#160;";
58463                         }
58464                         //Roo.log(c);
58465                          if(c.has_editor){
58466                             p.css += ' x-grid-editable-cell';
58467                         }
58468                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
58469                             p.css += ' x-grid-dirty-cell' 
58470                         }
58471                         
58472                         var markup = ct.apply(p);
58473                         if(!c.locked){
58474                             cb[cb.length] = markup;
58475                         }else{
58476                             lcb[lcb.length] = markup;
58477                         }
58478                     }
58479                     var alt = [];
58480                     if(stripe && ((rowIndex+1) % 2 == 0)){
58481                         alt.push( "x-grid-row-alt");
58482                     }
58483                     if(r.dirty){
58484                         alt.push(" x-grid-dirty-row");
58485                     }
58486                     rp.cells = lcb;
58487                     if(this.getRowClass){
58488                         alt.push( this.getRowClass(r, rowIndex));
58489                     }
58490                     if (hasListener) {
58491                         rowcfg = {
58492                              
58493                             record: r,
58494                             rowIndex : rowIndex,
58495                             rowClass : ''
58496                         };
58497                         this.grid.fireEvent('rowclass', this, rowcfg);
58498                         alt.push(rowcfg.rowClass);
58499                     }
58500                     
58501                     rp.alt = alt.join(" ");
58502                     rp.cells = lcb.join("");
58503                     lbuf[lbuf.length] = rt.apply(rp);
58504                     rp.cells = cb.join("");
58505                     buf[buf.length] =  rt.apply(rp);
58506                 }
58507                 return [lbuf.join(""), buf.join("")];
58508             },
58509
58510     renderBody : function(){
58511         var markup = this.renderRows();
58512         var bt = this.templates.body;
58513         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
58514     },
58515
58516     /**
58517      * Refreshes the grid
58518      * @param {Boolean} headersToo
58519      */
58520     refresh : function(headersToo){
58521         this.fireEvent("beforerefresh", this);
58522         this.grid.stopEditing();
58523         var result = this.renderBody();
58524         this.lockedBody.update(result[0]);
58525         this.mainBody.update(result[1]);
58526         if(headersToo === true){
58527             this.updateHeaders();
58528             this.updateColumns();
58529             this.updateSplitters();
58530             this.updateHeaderSortState();
58531         }
58532         this.syncRowHeights();
58533         this.layout();
58534         this.fireEvent("refresh", this);
58535     },
58536
58537     handleColumnMove : function(cm, oldIndex, newIndex){
58538         this.indexMap = null;
58539         var s = this.getScrollState();
58540         this.refresh(true);
58541         this.restoreScroll(s);
58542         this.afterMove(newIndex);
58543     },
58544
58545     afterMove : function(colIndex){
58546         if(this.enableMoveAnim && Roo.enableFx){
58547             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
58548         }
58549         // if multisort - fix sortOrder, and reload..
58550         if (this.grid.dataSource.multiSort) {
58551             // the we can call sort again..
58552             var dm = this.grid.dataSource;
58553             var cm = this.grid.colModel;
58554             var so = [];
58555             for(var i = 0; i < cm.config.length; i++ ) {
58556                 
58557                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
58558                     continue; // dont' bother, it's not in sort list or being set.
58559                 }
58560                 
58561                 so.push(cm.config[i].dataIndex);
58562             };
58563             dm.sortOrder = so;
58564             dm.load(dm.lastOptions);
58565             
58566             
58567         }
58568         
58569     },
58570
58571     updateCell : function(dm, rowIndex, dataIndex){
58572         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
58573         if(typeof colIndex == "undefined"){ // not present in grid
58574             return;
58575         }
58576         var cm = this.grid.colModel;
58577         var cell = this.getCell(rowIndex, colIndex);
58578         var cellText = this.getCellText(rowIndex, colIndex);
58579
58580         var p = {
58581             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
58582             id : cm.getColumnId(colIndex),
58583             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
58584         };
58585         var renderer = cm.getRenderer(colIndex);
58586         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
58587         if(typeof val == "undefined" || val === "") {
58588             val = "&#160;";
58589         }
58590         cellText.innerHTML = val;
58591         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
58592         this.syncRowHeights(rowIndex, rowIndex);
58593     },
58594
58595     calcColumnWidth : function(colIndex, maxRowsToMeasure){
58596         var maxWidth = 0;
58597         if(this.grid.autoSizeHeaders){
58598             var h = this.getHeaderCellMeasure(colIndex);
58599             maxWidth = Math.max(maxWidth, h.scrollWidth);
58600         }
58601         var tb, index;
58602         if(this.cm.isLocked(colIndex)){
58603             tb = this.getLockedTable();
58604             index = colIndex;
58605         }else{
58606             tb = this.getBodyTable();
58607             index = colIndex - this.cm.getLockedCount();
58608         }
58609         if(tb && tb.rows){
58610             var rows = tb.rows;
58611             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
58612             for(var i = 0; i < stopIndex; i++){
58613                 var cell = rows[i].childNodes[index].firstChild;
58614                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
58615             }
58616         }
58617         return maxWidth + /*margin for error in IE*/ 5;
58618     },
58619     /**
58620      * Autofit a column to its content.
58621      * @param {Number} colIndex
58622      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
58623      */
58624      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
58625          if(this.cm.isHidden(colIndex)){
58626              return; // can't calc a hidden column
58627          }
58628         if(forceMinSize){
58629             var cid = this.cm.getColumnId(colIndex);
58630             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
58631            if(this.grid.autoSizeHeaders){
58632                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
58633            }
58634         }
58635         var newWidth = this.calcColumnWidth(colIndex);
58636         this.cm.setColumnWidth(colIndex,
58637             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
58638         if(!suppressEvent){
58639             this.grid.fireEvent("columnresize", colIndex, newWidth);
58640         }
58641     },
58642
58643     /**
58644      * Autofits all columns to their content and then expands to fit any extra space in the grid
58645      */
58646      autoSizeColumns : function(){
58647         var cm = this.grid.colModel;
58648         var colCount = cm.getColumnCount();
58649         for(var i = 0; i < colCount; i++){
58650             this.autoSizeColumn(i, true, true);
58651         }
58652         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
58653             this.fitColumns();
58654         }else{
58655             this.updateColumns();
58656             this.layout();
58657         }
58658     },
58659
58660     /**
58661      * Autofits all columns to the grid's width proportionate with their current size
58662      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
58663      */
58664     fitColumns : function(reserveScrollSpace){
58665         var cm = this.grid.colModel;
58666         var colCount = cm.getColumnCount();
58667         var cols = [];
58668         var width = 0;
58669         var i, w;
58670         for (i = 0; i < colCount; i++){
58671             if(!cm.isHidden(i) && !cm.isFixed(i)){
58672                 w = cm.getColumnWidth(i);
58673                 cols.push(i);
58674                 cols.push(w);
58675                 width += w;
58676             }
58677         }
58678         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
58679         if(reserveScrollSpace){
58680             avail -= 17;
58681         }
58682         var frac = (avail - cm.getTotalWidth())/width;
58683         while (cols.length){
58684             w = cols.pop();
58685             i = cols.pop();
58686             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
58687         }
58688         this.updateColumns();
58689         this.layout();
58690     },
58691
58692     onRowSelect : function(rowIndex){
58693         var row = this.getRowComposite(rowIndex);
58694         row.addClass("x-grid-row-selected");
58695     },
58696
58697     onRowDeselect : function(rowIndex){
58698         var row = this.getRowComposite(rowIndex);
58699         row.removeClass("x-grid-row-selected");
58700     },
58701
58702     onCellSelect : function(row, col){
58703         var cell = this.getCell(row, col);
58704         if(cell){
58705             Roo.fly(cell).addClass("x-grid-cell-selected");
58706         }
58707     },
58708
58709     onCellDeselect : function(row, col){
58710         var cell = this.getCell(row, col);
58711         if(cell){
58712             Roo.fly(cell).removeClass("x-grid-cell-selected");
58713         }
58714     },
58715
58716     updateHeaderSortState : function(){
58717         
58718         // sort state can be single { field: xxx, direction : yyy}
58719         // or   { xxx=>ASC , yyy : DESC ..... }
58720         
58721         var mstate = {};
58722         if (!this.ds.multiSort) { 
58723             var state = this.ds.getSortState();
58724             if(!state){
58725                 return;
58726             }
58727             mstate[state.field] = state.direction;
58728             // FIXME... - this is not used here.. but might be elsewhere..
58729             this.sortState = state;
58730             
58731         } else {
58732             mstate = this.ds.sortToggle;
58733         }
58734         //remove existing sort classes..
58735         
58736         var sc = this.sortClasses;
58737         var hds = this.el.select(this.headerSelector).removeClass(sc);
58738         
58739         for(var f in mstate) {
58740         
58741             var sortColumn = this.cm.findColumnIndex(f);
58742             
58743             if(sortColumn != -1){
58744                 var sortDir = mstate[f];        
58745                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
58746             }
58747         }
58748         
58749          
58750         
58751     },
58752
58753
58754     handleHeaderClick : function(g, index,e){
58755         
58756         Roo.log("header click");
58757         
58758         if (Roo.isTouch) {
58759             // touch events on header are handled by context
58760             this.handleHdCtx(g,index,e);
58761             return;
58762         }
58763         
58764         
58765         if(this.headersDisabled){
58766             return;
58767         }
58768         var dm = g.dataSource, cm = g.colModel;
58769         if(!cm.isSortable(index)){
58770             return;
58771         }
58772         g.stopEditing();
58773         
58774         if (dm.multiSort) {
58775             // update the sortOrder
58776             var so = [];
58777             for(var i = 0; i < cm.config.length; i++ ) {
58778                 
58779                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
58780                     continue; // dont' bother, it's not in sort list or being set.
58781                 }
58782                 
58783                 so.push(cm.config[i].dataIndex);
58784             };
58785             dm.sortOrder = so;
58786         }
58787         
58788         
58789         dm.sort(cm.getDataIndex(index));
58790     },
58791
58792
58793     destroy : function(){
58794         if(this.colMenu){
58795             this.colMenu.removeAll();
58796             Roo.menu.MenuMgr.unregister(this.colMenu);
58797             this.colMenu.getEl().remove();
58798             delete this.colMenu;
58799         }
58800         if(this.hmenu){
58801             this.hmenu.removeAll();
58802             Roo.menu.MenuMgr.unregister(this.hmenu);
58803             this.hmenu.getEl().remove();
58804             delete this.hmenu;
58805         }
58806         if(this.grid.enableColumnMove){
58807             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58808             if(dds){
58809                 for(var dd in dds){
58810                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
58811                         var elid = dds[dd].dragElId;
58812                         dds[dd].unreg();
58813                         Roo.get(elid).remove();
58814                     } else if(dds[dd].config.isTarget){
58815                         dds[dd].proxyTop.remove();
58816                         dds[dd].proxyBottom.remove();
58817                         dds[dd].unreg();
58818                     }
58819                     if(Roo.dd.DDM.locationCache[dd]){
58820                         delete Roo.dd.DDM.locationCache[dd];
58821                     }
58822                 }
58823                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58824             }
58825         }
58826         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
58827         this.bind(null, null);
58828         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
58829     },
58830
58831     handleLockChange : function(){
58832         this.refresh(true);
58833     },
58834
58835     onDenyColumnLock : function(){
58836
58837     },
58838
58839     onDenyColumnHide : function(){
58840
58841     },
58842
58843     handleHdMenuClick : function(item){
58844         var index = this.hdCtxIndex;
58845         var cm = this.cm, ds = this.ds;
58846         switch(item.id){
58847             case "asc":
58848                 ds.sort(cm.getDataIndex(index), "ASC");
58849                 break;
58850             case "desc":
58851                 ds.sort(cm.getDataIndex(index), "DESC");
58852                 break;
58853             case "lock":
58854                 var lc = cm.getLockedCount();
58855                 if(cm.getColumnCount(true) <= lc+1){
58856                     this.onDenyColumnLock();
58857                     return;
58858                 }
58859                 if(lc != index){
58860                     cm.setLocked(index, true, true);
58861                     cm.moveColumn(index, lc);
58862                     this.grid.fireEvent("columnmove", index, lc);
58863                 }else{
58864                     cm.setLocked(index, true);
58865                 }
58866             break;
58867             case "unlock":
58868                 var lc = cm.getLockedCount();
58869                 if((lc-1) != index){
58870                     cm.setLocked(index, false, true);
58871                     cm.moveColumn(index, lc-1);
58872                     this.grid.fireEvent("columnmove", index, lc-1);
58873                 }else{
58874                     cm.setLocked(index, false);
58875                 }
58876             break;
58877             case 'wider': // used to expand cols on touch..
58878             case 'narrow':
58879                 var cw = cm.getColumnWidth(index);
58880                 cw += (item.id == 'wider' ? 1 : -1) * 50;
58881                 cw = Math.max(0, cw);
58882                 cw = Math.min(cw,4000);
58883                 cm.setColumnWidth(index, cw);
58884                 break;
58885                 
58886             default:
58887                 index = cm.getIndexById(item.id.substr(4));
58888                 if(index != -1){
58889                     if(item.checked && cm.getColumnCount(true) <= 1){
58890                         this.onDenyColumnHide();
58891                         return false;
58892                     }
58893                     cm.setHidden(index, item.checked);
58894                 }
58895         }
58896         return true;
58897     },
58898
58899     beforeColMenuShow : function(){
58900         var cm = this.cm,  colCount = cm.getColumnCount();
58901         this.colMenu.removeAll();
58902         for(var i = 0; i < colCount; i++){
58903             this.colMenu.add(new Roo.menu.CheckItem({
58904                 id: "col-"+cm.getColumnId(i),
58905                 text: cm.getColumnHeader(i),
58906                 checked: !cm.isHidden(i),
58907                 hideOnClick:false
58908             }));
58909         }
58910     },
58911
58912     handleHdCtx : function(g, index, e){
58913         e.stopEvent();
58914         var hd = this.getHeaderCell(index);
58915         this.hdCtxIndex = index;
58916         var ms = this.hmenu.items, cm = this.cm;
58917         ms.get("asc").setDisabled(!cm.isSortable(index));
58918         ms.get("desc").setDisabled(!cm.isSortable(index));
58919         if(this.grid.enableColLock !== false){
58920             ms.get("lock").setDisabled(cm.isLocked(index));
58921             ms.get("unlock").setDisabled(!cm.isLocked(index));
58922         }
58923         this.hmenu.show(hd, "tl-bl");
58924     },
58925
58926     handleHdOver : function(e){
58927         var hd = this.findHeaderCell(e.getTarget());
58928         if(hd && !this.headersDisabled){
58929             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
58930                this.fly(hd).addClass("x-grid-hd-over");
58931             }
58932         }
58933     },
58934
58935     handleHdOut : function(e){
58936         var hd = this.findHeaderCell(e.getTarget());
58937         if(hd){
58938             this.fly(hd).removeClass("x-grid-hd-over");
58939         }
58940     },
58941
58942     handleSplitDblClick : function(e, t){
58943         var i = this.getCellIndex(t);
58944         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
58945             this.autoSizeColumn(i, true);
58946             this.layout();
58947         }
58948     },
58949
58950     render : function(){
58951
58952         var cm = this.cm;
58953         var colCount = cm.getColumnCount();
58954
58955         if(this.grid.monitorWindowResize === true){
58956             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58957         }
58958         var header = this.renderHeaders();
58959         var body = this.templates.body.apply({rows:""});
58960         var html = this.templates.master.apply({
58961             lockedBody: body,
58962             body: body,
58963             lockedHeader: header[0],
58964             header: header[1]
58965         });
58966
58967         //this.updateColumns();
58968
58969         this.grid.getGridEl().dom.innerHTML = html;
58970
58971         this.initElements();
58972         
58973         // a kludge to fix the random scolling effect in webkit
58974         this.el.on("scroll", function() {
58975             this.el.dom.scrollTop=0; // hopefully not recursive..
58976         },this);
58977
58978         this.scroller.on("scroll", this.handleScroll, this);
58979         this.lockedBody.on("mousewheel", this.handleWheel, this);
58980         this.mainBody.on("mousewheel", this.handleWheel, this);
58981
58982         this.mainHd.on("mouseover", this.handleHdOver, this);
58983         this.mainHd.on("mouseout", this.handleHdOut, this);
58984         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
58985                 {delegate: "."+this.splitClass});
58986
58987         this.lockedHd.on("mouseover", this.handleHdOver, this);
58988         this.lockedHd.on("mouseout", this.handleHdOut, this);
58989         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
58990                 {delegate: "."+this.splitClass});
58991
58992         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
58993             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58994         }
58995
58996         this.updateSplitters();
58997
58998         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
58999             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59000             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59001         }
59002
59003         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
59004             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
59005             this.hmenu.add(
59006                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
59007                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
59008             );
59009             if(this.grid.enableColLock !== false){
59010                 this.hmenu.add('-',
59011                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
59012                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
59013                 );
59014             }
59015             if (Roo.isTouch) {
59016                  this.hmenu.add('-',
59017                     {id:"wider", text: this.columnsWiderText},
59018                     {id:"narrow", text: this.columnsNarrowText }
59019                 );
59020                 
59021                  
59022             }
59023             
59024             if(this.grid.enableColumnHide !== false){
59025
59026                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
59027                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
59028                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
59029
59030                 this.hmenu.add('-',
59031                     {id:"columns", text: this.columnsText, menu: this.colMenu}
59032                 );
59033             }
59034             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
59035
59036             this.grid.on("headercontextmenu", this.handleHdCtx, this);
59037         }
59038
59039         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
59040             this.dd = new Roo.grid.GridDragZone(this.grid, {
59041                 ddGroup : this.grid.ddGroup || 'GridDD'
59042             });
59043             
59044         }
59045
59046         /*
59047         for(var i = 0; i < colCount; i++){
59048             if(cm.isHidden(i)){
59049                 this.hideColumn(i);
59050             }
59051             if(cm.config[i].align){
59052                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
59053                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
59054             }
59055         }*/
59056         
59057         this.updateHeaderSortState();
59058
59059         this.beforeInitialResize();
59060         this.layout(true);
59061
59062         // two part rendering gives faster view to the user
59063         this.renderPhase2.defer(1, this);
59064     },
59065
59066     renderPhase2 : function(){
59067         // render the rows now
59068         this.refresh();
59069         if(this.grid.autoSizeColumns){
59070             this.autoSizeColumns();
59071         }
59072     },
59073
59074     beforeInitialResize : function(){
59075
59076     },
59077
59078     onColumnSplitterMoved : function(i, w){
59079         this.userResized = true;
59080         var cm = this.grid.colModel;
59081         cm.setColumnWidth(i, w, true);
59082         var cid = cm.getColumnId(i);
59083         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59084         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59085         this.updateSplitters();
59086         this.layout();
59087         this.grid.fireEvent("columnresize", i, w);
59088     },
59089
59090     syncRowHeights : function(startIndex, endIndex){
59091         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
59092             startIndex = startIndex || 0;
59093             var mrows = this.getBodyTable().rows;
59094             var lrows = this.getLockedTable().rows;
59095             var len = mrows.length-1;
59096             endIndex = Math.min(endIndex || len, len);
59097             for(var i = startIndex; i <= endIndex; i++){
59098                 var m = mrows[i], l = lrows[i];
59099                 var h = Math.max(m.offsetHeight, l.offsetHeight);
59100                 m.style.height = l.style.height = h + "px";
59101             }
59102         }
59103     },
59104
59105     layout : function(initialRender, is2ndPass)
59106     {
59107         var g = this.grid;
59108         var auto = g.autoHeight;
59109         var scrollOffset = 16;
59110         var c = g.getGridEl(), cm = this.cm,
59111                 expandCol = g.autoExpandColumn,
59112                 gv = this;
59113         //c.beginMeasure();
59114
59115         if(!c.dom.offsetWidth){ // display:none?
59116             if(initialRender){
59117                 this.lockedWrap.show();
59118                 this.mainWrap.show();
59119             }
59120             return;
59121         }
59122
59123         var hasLock = this.cm.isLocked(0);
59124
59125         var tbh = this.headerPanel.getHeight();
59126         var bbh = this.footerPanel.getHeight();
59127
59128         if(auto){
59129             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
59130             var newHeight = ch + c.getBorderWidth("tb");
59131             if(g.maxHeight){
59132                 newHeight = Math.min(g.maxHeight, newHeight);
59133             }
59134             c.setHeight(newHeight);
59135         }
59136
59137         if(g.autoWidth){
59138             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
59139         }
59140
59141         var s = this.scroller;
59142
59143         var csize = c.getSize(true);
59144
59145         this.el.setSize(csize.width, csize.height);
59146
59147         this.headerPanel.setWidth(csize.width);
59148         this.footerPanel.setWidth(csize.width);
59149
59150         var hdHeight = this.mainHd.getHeight();
59151         var vw = csize.width;
59152         var vh = csize.height - (tbh + bbh);
59153
59154         s.setSize(vw, vh);
59155
59156         var bt = this.getBodyTable();
59157         
59158         if(cm.getLockedCount() == cm.config.length){
59159             bt = this.getLockedTable();
59160         }
59161         
59162         var ltWidth = hasLock ?
59163                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
59164
59165         var scrollHeight = bt.offsetHeight;
59166         var scrollWidth = ltWidth + bt.offsetWidth;
59167         var vscroll = false, hscroll = false;
59168
59169         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
59170
59171         var lw = this.lockedWrap, mw = this.mainWrap;
59172         var lb = this.lockedBody, mb = this.mainBody;
59173
59174         setTimeout(function(){
59175             var t = s.dom.offsetTop;
59176             var w = s.dom.clientWidth,
59177                 h = s.dom.clientHeight;
59178
59179             lw.setTop(t);
59180             lw.setSize(ltWidth, h);
59181
59182             mw.setLeftTop(ltWidth, t);
59183             mw.setSize(w-ltWidth, h);
59184
59185             lb.setHeight(h-hdHeight);
59186             mb.setHeight(h-hdHeight);
59187
59188             if(is2ndPass !== true && !gv.userResized && expandCol){
59189                 // high speed resize without full column calculation
59190                 
59191                 var ci = cm.getIndexById(expandCol);
59192                 if (ci < 0) {
59193                     ci = cm.findColumnIndex(expandCol);
59194                 }
59195                 ci = Math.max(0, ci); // make sure it's got at least the first col.
59196                 var expandId = cm.getColumnId(ci);
59197                 var  tw = cm.getTotalWidth(false);
59198                 var currentWidth = cm.getColumnWidth(ci);
59199                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
59200                 if(currentWidth != cw){
59201                     cm.setColumnWidth(ci, cw, true);
59202                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59203                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59204                     gv.updateSplitters();
59205                     gv.layout(false, true);
59206                 }
59207             }
59208
59209             if(initialRender){
59210                 lw.show();
59211                 mw.show();
59212             }
59213             //c.endMeasure();
59214         }, 10);
59215     },
59216
59217     onWindowResize : function(){
59218         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
59219             return;
59220         }
59221         this.layout();
59222     },
59223
59224     appendFooter : function(parentEl){
59225         return null;
59226     },
59227
59228     sortAscText : "Sort Ascending",
59229     sortDescText : "Sort Descending",
59230     lockText : "Lock Column",
59231     unlockText : "Unlock Column",
59232     columnsText : "Columns",
59233  
59234     columnsWiderText : "Wider",
59235     columnsNarrowText : "Thinner"
59236 });
59237
59238
59239 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
59240     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
59241     this.proxy.el.addClass('x-grid3-col-dd');
59242 };
59243
59244 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
59245     handleMouseDown : function(e){
59246
59247     },
59248
59249     callHandleMouseDown : function(e){
59250         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
59251     }
59252 });
59253 /*
59254  * Based on:
59255  * Ext JS Library 1.1.1
59256  * Copyright(c) 2006-2007, Ext JS, LLC.
59257  *
59258  * Originally Released Under LGPL - original licence link has changed is not relivant.
59259  *
59260  * Fork - LGPL
59261  * <script type="text/javascript">
59262  */
59263  /**
59264  * @extends Roo.dd.DDProxy
59265  * @class Roo.grid.SplitDragZone
59266  * Support for Column Header resizing
59267  * @constructor
59268  * @param {Object} config
59269  */
59270 // private
59271 // This is a support class used internally by the Grid components
59272 Roo.grid.SplitDragZone = function(grid, hd, hd2){
59273     this.grid = grid;
59274     this.view = grid.getView();
59275     this.proxy = this.view.resizeProxy;
59276     Roo.grid.SplitDragZone.superclass.constructor.call(
59277         this,
59278         hd, // ID
59279         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
59280         {  // CONFIG
59281             dragElId : Roo.id(this.proxy.dom),
59282             resizeFrame:false
59283         }
59284     );
59285     
59286     this.setHandleElId(Roo.id(hd));
59287     if (hd2 !== false) {
59288         this.setOuterHandleElId(Roo.id(hd2));
59289     }
59290     
59291     this.scroll = false;
59292 };
59293 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
59294     fly: Roo.Element.fly,
59295
59296     b4StartDrag : function(x, y){
59297         this.view.headersDisabled = true;
59298         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
59299                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
59300         );
59301         this.proxy.setHeight(h);
59302         
59303         // for old system colWidth really stored the actual width?
59304         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
59305         // which in reality did not work.. - it worked only for fixed sizes
59306         // for resizable we need to use actual sizes.
59307         var w = this.cm.getColumnWidth(this.cellIndex);
59308         if (!this.view.mainWrap) {
59309             // bootstrap.
59310             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
59311         }
59312         
59313         
59314         
59315         // this was w-this.grid.minColumnWidth;
59316         // doesnt really make sense? - w = thie curren width or the rendered one?
59317         var minw = Math.max(w-this.grid.minColumnWidth, 0);
59318         this.resetConstraints();
59319         this.setXConstraint(minw, 1000);
59320         this.setYConstraint(0, 0);
59321         this.minX = x - minw;
59322         this.maxX = x + 1000;
59323         this.startPos = x;
59324         if (!this.view.mainWrap) { // this is Bootstrap code..
59325             this.getDragEl().style.display='block';
59326         }
59327         
59328         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
59329     },
59330
59331
59332     handleMouseDown : function(e){
59333         ev = Roo.EventObject.setEvent(e);
59334         var t = this.fly(ev.getTarget());
59335         if(t.hasClass("x-grid-split")){
59336             this.cellIndex = this.view.getCellIndex(t.dom);
59337             this.split = t.dom;
59338             this.cm = this.grid.colModel;
59339             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
59340                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
59341             }
59342         }
59343     },
59344
59345     endDrag : function(e){
59346         this.view.headersDisabled = false;
59347         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
59348         var diff = endX - this.startPos;
59349         // 
59350         var w = this.cm.getColumnWidth(this.cellIndex);
59351         if (!this.view.mainWrap) {
59352             w = 0;
59353         }
59354         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
59355     },
59356
59357     autoOffset : function(){
59358         this.setDelta(0,0);
59359     }
59360 });/*
59361  * Based on:
59362  * Ext JS Library 1.1.1
59363  * Copyright(c) 2006-2007, Ext JS, LLC.
59364  *
59365  * Originally Released Under LGPL - original licence link has changed is not relivant.
59366  *
59367  * Fork - LGPL
59368  * <script type="text/javascript">
59369  */
59370  
59371 // private
59372 // This is a support class used internally by the Grid components
59373 Roo.grid.GridDragZone = function(grid, config){
59374     this.view = grid.getView();
59375     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59376     if(this.view.lockedBody){
59377         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59378         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59379     }
59380     this.scroll = false;
59381     this.grid = grid;
59382     this.ddel = document.createElement('div');
59383     this.ddel.className = 'x-grid-dd-wrap';
59384 };
59385
59386 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59387     ddGroup : "GridDD",
59388
59389     getDragData : function(e){
59390         var t = Roo.lib.Event.getTarget(e);
59391         var rowIndex = this.view.findRowIndex(t);
59392         var sm = this.grid.selModel;
59393             
59394         //Roo.log(rowIndex);
59395         
59396         if (sm.getSelectedCell) {
59397             // cell selection..
59398             if (!sm.getSelectedCell()) {
59399                 return false;
59400             }
59401             if (rowIndex != sm.getSelectedCell()[0]) {
59402                 return false;
59403             }
59404         
59405         }
59406         if (sm.getSelections && sm.getSelections().length < 1) {
59407             return false;
59408         }
59409         
59410         
59411         // before it used to all dragging of unseleted... - now we dont do that.
59412         if(rowIndex !== false){
59413             
59414             // if editorgrid.. 
59415             
59416             
59417             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59418                
59419             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59420               //  
59421             //}
59422             if (e.hasModifier()){
59423                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59424             }
59425             
59426             Roo.log("getDragData");
59427             
59428             return {
59429                 grid: this.grid,
59430                 ddel: this.ddel,
59431                 rowIndex: rowIndex,
59432                 selections: sm.getSelections ? sm.getSelections() : (
59433                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59434             };
59435         }
59436         return false;
59437     },
59438     
59439     
59440     onInitDrag : function(e){
59441         var data = this.dragData;
59442         this.ddel.innerHTML = this.grid.getDragDropText();
59443         this.proxy.update(this.ddel);
59444         // fire start drag?
59445     },
59446
59447     afterRepair : function(){
59448         this.dragging = false;
59449     },
59450
59451     getRepairXY : function(e, data){
59452         return false;
59453     },
59454
59455     onEndDrag : function(data, e){
59456         // fire end drag?
59457     },
59458
59459     onValidDrop : function(dd, e, id){
59460         // fire drag drop?
59461         this.hideProxy();
59462     },
59463
59464     beforeInvalidDrop : function(e, id){
59465
59466     }
59467 });/*
59468  * Based on:
59469  * Ext JS Library 1.1.1
59470  * Copyright(c) 2006-2007, Ext JS, LLC.
59471  *
59472  * Originally Released Under LGPL - original licence link has changed is not relivant.
59473  *
59474  * Fork - LGPL
59475  * <script type="text/javascript">
59476  */
59477  
59478
59479 /**
59480  * @class Roo.grid.ColumnModel
59481  * @extends Roo.util.Observable
59482  * This is the default implementation of a ColumnModel used by the Grid. It defines
59483  * the columns in the grid.
59484  * <br>Usage:<br>
59485  <pre><code>
59486  var colModel = new Roo.grid.ColumnModel([
59487         {header: "Ticker", width: 60, sortable: true, locked: true},
59488         {header: "Company Name", width: 150, sortable: true},
59489         {header: "Market Cap.", width: 100, sortable: true},
59490         {header: "$ Sales", width: 100, sortable: true, renderer: money},
59491         {header: "Employees", width: 100, sortable: true, resizable: false}
59492  ]);
59493  </code></pre>
59494  * <p>
59495  
59496  * The config options listed for this class are options which may appear in each
59497  * individual column definition.
59498  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
59499  * @constructor
59500  * @param {Object} config An Array of column config objects. See this class's
59501  * config objects for details.
59502 */
59503 Roo.grid.ColumnModel = function(config){
59504         /**
59505      * The config passed into the constructor
59506      */
59507     this.config = []; //config;
59508     this.lookup = {};
59509
59510     // if no id, create one
59511     // if the column does not have a dataIndex mapping,
59512     // map it to the order it is in the config
59513     for(var i = 0, len = config.length; i < len; i++){
59514         this.addColumn(config[i]);
59515         
59516     }
59517
59518     /**
59519      * The width of columns which have no width specified (defaults to 100)
59520      * @type Number
59521      */
59522     this.defaultWidth = 100;
59523
59524     /**
59525      * Default sortable of columns which have no sortable specified (defaults to false)
59526      * @type Boolean
59527      */
59528     this.defaultSortable = false;
59529
59530     this.addEvents({
59531         /**
59532              * @event widthchange
59533              * Fires when the width of a column changes.
59534              * @param {ColumnModel} this
59535              * @param {Number} columnIndex The column index
59536              * @param {Number} newWidth The new width
59537              */
59538             "widthchange": true,
59539         /**
59540              * @event headerchange
59541              * Fires when the text of a header changes.
59542              * @param {ColumnModel} this
59543              * @param {Number} columnIndex The column index
59544              * @param {Number} newText The new header text
59545              */
59546             "headerchange": true,
59547         /**
59548              * @event hiddenchange
59549              * Fires when a column is hidden or "unhidden".
59550              * @param {ColumnModel} this
59551              * @param {Number} columnIndex The column index
59552              * @param {Boolean} hidden true if hidden, false otherwise
59553              */
59554             "hiddenchange": true,
59555             /**
59556          * @event columnmoved
59557          * Fires when a column is moved.
59558          * @param {ColumnModel} this
59559          * @param {Number} oldIndex
59560          * @param {Number} newIndex
59561          */
59562         "columnmoved" : true,
59563         /**
59564          * @event columlockchange
59565          * Fires when a column's locked state is changed
59566          * @param {ColumnModel} this
59567          * @param {Number} colIndex
59568          * @param {Boolean} locked true if locked
59569          */
59570         "columnlockchange" : true
59571     });
59572     Roo.grid.ColumnModel.superclass.constructor.call(this);
59573 };
59574 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
59575     /**
59576      * @cfg {String} header The header text to display in the Grid view.
59577      */
59578         /**
59579      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
59580      */
59581         /**
59582      * @cfg {String} smHeader Header at Bootsrap Small width
59583      */
59584         /**
59585      * @cfg {String} mdHeader Header at Bootsrap Medium width
59586      */
59587         /**
59588      * @cfg {String} lgHeader Header at Bootsrap Large width
59589      */
59590         /**
59591      * @cfg {String} xlHeader Header at Bootsrap extra Large width
59592      */
59593     /**
59594      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
59595      * {@link Roo.data.Record} definition from which to draw the column's value. If not
59596      * specified, the column's index is used as an index into the Record's data Array.
59597      */
59598     /**
59599      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
59600      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
59601      */
59602     /**
59603      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
59604      * Defaults to the value of the {@link #defaultSortable} property.
59605      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
59606      */
59607     /**
59608      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
59609      */
59610     /**
59611      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
59612      */
59613     /**
59614      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
59615      */
59616     /**
59617      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
59618      */
59619     /**
59620      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
59621      * given the cell's data value. See {@link #setRenderer}. If not specified, the
59622      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
59623      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
59624      */
59625        /**
59626      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
59627      */
59628     /**
59629      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
59630      */
59631     /**
59632      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
59633      */
59634     /**
59635      * @cfg {String} cursor (Optional)
59636      */
59637     /**
59638      * @cfg {String} tooltip (Optional)
59639      */
59640     /**
59641      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
59642      */
59643     /**
59644      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
59645      */
59646     /**
59647      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
59648      */
59649     /**
59650      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
59651      */
59652         /**
59653      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
59654      */
59655     /**
59656      * Returns the id of the column at the specified index.
59657      * @param {Number} index The column index
59658      * @return {String} the id
59659      */
59660     getColumnId : function(index){
59661         return this.config[index].id;
59662     },
59663
59664     /**
59665      * Returns the column for a specified id.
59666      * @param {String} id The column id
59667      * @return {Object} the column
59668      */
59669     getColumnById : function(id){
59670         return this.lookup[id];
59671     },
59672
59673     
59674     /**
59675      * Returns the column Object for a specified dataIndex.
59676      * @param {String} dataIndex The column dataIndex
59677      * @return {Object|Boolean} the column or false if not found
59678      */
59679     getColumnByDataIndex: function(dataIndex){
59680         var index = this.findColumnIndex(dataIndex);
59681         return index > -1 ? this.config[index] : false;
59682     },
59683     
59684     /**
59685      * Returns the index for a specified column id.
59686      * @param {String} id The column id
59687      * @return {Number} the index, or -1 if not found
59688      */
59689     getIndexById : function(id){
59690         for(var i = 0, len = this.config.length; i < len; i++){
59691             if(this.config[i].id == id){
59692                 return i;
59693             }
59694         }
59695         return -1;
59696     },
59697     
59698     /**
59699      * Returns the index for a specified column dataIndex.
59700      * @param {String} dataIndex The column dataIndex
59701      * @return {Number} the index, or -1 if not found
59702      */
59703     
59704     findColumnIndex : function(dataIndex){
59705         for(var i = 0, len = this.config.length; i < len; i++){
59706             if(this.config[i].dataIndex == dataIndex){
59707                 return i;
59708             }
59709         }
59710         return -1;
59711     },
59712     
59713     
59714     moveColumn : function(oldIndex, newIndex){
59715         var c = this.config[oldIndex];
59716         this.config.splice(oldIndex, 1);
59717         this.config.splice(newIndex, 0, c);
59718         this.dataMap = null;
59719         this.fireEvent("columnmoved", this, oldIndex, newIndex);
59720     },
59721
59722     isLocked : function(colIndex){
59723         return this.config[colIndex].locked === true;
59724     },
59725
59726     setLocked : function(colIndex, value, suppressEvent){
59727         if(this.isLocked(colIndex) == value){
59728             return;
59729         }
59730         this.config[colIndex].locked = value;
59731         if(!suppressEvent){
59732             this.fireEvent("columnlockchange", this, colIndex, value);
59733         }
59734     },
59735
59736     getTotalLockedWidth : function(){
59737         var totalWidth = 0;
59738         for(var i = 0; i < this.config.length; i++){
59739             if(this.isLocked(i) && !this.isHidden(i)){
59740                 this.totalWidth += this.getColumnWidth(i);
59741             }
59742         }
59743         return totalWidth;
59744     },
59745
59746     getLockedCount : function(){
59747         for(var i = 0, len = this.config.length; i < len; i++){
59748             if(!this.isLocked(i)){
59749                 return i;
59750             }
59751         }
59752         
59753         return this.config.length;
59754     },
59755
59756     /**
59757      * Returns the number of columns.
59758      * @return {Number}
59759      */
59760     getColumnCount : function(visibleOnly){
59761         if(visibleOnly === true){
59762             var c = 0;
59763             for(var i = 0, len = this.config.length; i < len; i++){
59764                 if(!this.isHidden(i)){
59765                     c++;
59766                 }
59767             }
59768             return c;
59769         }
59770         return this.config.length;
59771     },
59772
59773     /**
59774      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
59775      * @param {Function} fn
59776      * @param {Object} scope (optional)
59777      * @return {Array} result
59778      */
59779     getColumnsBy : function(fn, scope){
59780         var r = [];
59781         for(var i = 0, len = this.config.length; i < len; i++){
59782             var c = this.config[i];
59783             if(fn.call(scope||this, c, i) === true){
59784                 r[r.length] = c;
59785             }
59786         }
59787         return r;
59788     },
59789
59790     /**
59791      * Returns true if the specified column is sortable.
59792      * @param {Number} col The column index
59793      * @return {Boolean}
59794      */
59795     isSortable : function(col){
59796         if(typeof this.config[col].sortable == "undefined"){
59797             return this.defaultSortable;
59798         }
59799         return this.config[col].sortable;
59800     },
59801
59802     /**
59803      * Returns the rendering (formatting) function defined for the column.
59804      * @param {Number} col The column index.
59805      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
59806      */
59807     getRenderer : function(col){
59808         if(!this.config[col].renderer){
59809             return Roo.grid.ColumnModel.defaultRenderer;
59810         }
59811         return this.config[col].renderer;
59812     },
59813
59814     /**
59815      * Sets the rendering (formatting) function for a column.
59816      * @param {Number} col The column index
59817      * @param {Function} fn The function to use to process the cell's raw data
59818      * to return HTML markup for the grid view. The render function is called with
59819      * the following parameters:<ul>
59820      * <li>Data value.</li>
59821      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
59822      * <li>css A CSS style string to apply to the table cell.</li>
59823      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
59824      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
59825      * <li>Row index</li>
59826      * <li>Column index</li>
59827      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
59828      */
59829     setRenderer : function(col, fn){
59830         this.config[col].renderer = fn;
59831     },
59832
59833     /**
59834      * Returns the width for the specified column.
59835      * @param {Number} col The column index
59836      * @param (optional) {String} gridSize bootstrap width size.
59837      * @return {Number}
59838      */
59839     getColumnWidth : function(col, gridSize)
59840         {
59841                 var cfg = this.config[col];
59842                 
59843                 if (typeof(gridSize) == 'undefined') {
59844                         return cfg.width * 1 || this.defaultWidth;
59845                 }
59846                 if (gridSize === false) { // if we set it..
59847                         return cfg.width || false;
59848                 }
59849                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
59850                 
59851                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
59852                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
59853                                 continue;
59854                         }
59855                         return cfg[ sizes[i] ];
59856                 }
59857                 return 1;
59858                 
59859     },
59860
59861     /**
59862      * Sets the width for a column.
59863      * @param {Number} col The column index
59864      * @param {Number} width The new width
59865      */
59866     setColumnWidth : function(col, width, suppressEvent){
59867         this.config[col].width = width;
59868         this.totalWidth = null;
59869         if(!suppressEvent){
59870              this.fireEvent("widthchange", this, col, width);
59871         }
59872     },
59873
59874     /**
59875      * Returns the total width of all columns.
59876      * @param {Boolean} includeHidden True to include hidden column widths
59877      * @return {Number}
59878      */
59879     getTotalWidth : function(includeHidden){
59880         if(!this.totalWidth){
59881             this.totalWidth = 0;
59882             for(var i = 0, len = this.config.length; i < len; i++){
59883                 if(includeHidden || !this.isHidden(i)){
59884                     this.totalWidth += this.getColumnWidth(i);
59885                 }
59886             }
59887         }
59888         return this.totalWidth;
59889     },
59890
59891     /**
59892      * Returns the header for the specified column.
59893      * @param {Number} col The column index
59894      * @return {String}
59895      */
59896     getColumnHeader : function(col){
59897         return this.config[col].header;
59898     },
59899
59900     /**
59901      * Sets the header for a column.
59902      * @param {Number} col The column index
59903      * @param {String} header The new header
59904      */
59905     setColumnHeader : function(col, header){
59906         this.config[col].header = header;
59907         this.fireEvent("headerchange", this, col, header);
59908     },
59909
59910     /**
59911      * Returns the tooltip for the specified column.
59912      * @param {Number} col The column index
59913      * @return {String}
59914      */
59915     getColumnTooltip : function(col){
59916             return this.config[col].tooltip;
59917     },
59918     /**
59919      * Sets the tooltip for a column.
59920      * @param {Number} col The column index
59921      * @param {String} tooltip The new tooltip
59922      */
59923     setColumnTooltip : function(col, tooltip){
59924             this.config[col].tooltip = tooltip;
59925     },
59926
59927     /**
59928      * Returns the dataIndex for the specified column.
59929      * @param {Number} col The column index
59930      * @return {Number}
59931      */
59932     getDataIndex : function(col){
59933         return this.config[col].dataIndex;
59934     },
59935
59936     /**
59937      * Sets the dataIndex for a column.
59938      * @param {Number} col The column index
59939      * @param {Number} dataIndex The new dataIndex
59940      */
59941     setDataIndex : function(col, dataIndex){
59942         this.config[col].dataIndex = dataIndex;
59943     },
59944
59945     
59946     
59947     /**
59948      * Returns true if the cell is editable.
59949      * @param {Number} colIndex The column index
59950      * @param {Number} rowIndex The row index - this is nto actually used..?
59951      * @return {Boolean}
59952      */
59953     isCellEditable : function(colIndex, rowIndex){
59954         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
59955     },
59956
59957     /**
59958      * Returns the editor defined for the cell/column.
59959      * return false or null to disable editing.
59960      * @param {Number} colIndex The column index
59961      * @param {Number} rowIndex The row index
59962      * @return {Object}
59963      */
59964     getCellEditor : function(colIndex, rowIndex){
59965         return this.config[colIndex].editor;
59966     },
59967
59968     /**
59969      * Sets if a column is editable.
59970      * @param {Number} col The column index
59971      * @param {Boolean} editable True if the column is editable
59972      */
59973     setEditable : function(col, editable){
59974         this.config[col].editable = editable;
59975     },
59976
59977
59978     /**
59979      * Returns true if the column is hidden.
59980      * @param {Number} colIndex The column index
59981      * @return {Boolean}
59982      */
59983     isHidden : function(colIndex){
59984         return this.config[colIndex].hidden;
59985     },
59986
59987
59988     /**
59989      * Returns true if the column width cannot be changed
59990      */
59991     isFixed : function(colIndex){
59992         return this.config[colIndex].fixed;
59993     },
59994
59995     /**
59996      * Returns true if the column can be resized
59997      * @return {Boolean}
59998      */
59999     isResizable : function(colIndex){
60000         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
60001     },
60002     /**
60003      * Sets if a column is hidden.
60004      * @param {Number} colIndex The column index
60005      * @param {Boolean} hidden True if the column is hidden
60006      */
60007     setHidden : function(colIndex, hidden){
60008         this.config[colIndex].hidden = hidden;
60009         this.totalWidth = null;
60010         this.fireEvent("hiddenchange", this, colIndex, hidden);
60011     },
60012
60013     /**
60014      * Sets the editor for a column.
60015      * @param {Number} col The column index
60016      * @param {Object} editor The editor object
60017      */
60018     setEditor : function(col, editor){
60019         this.config[col].editor = editor;
60020     },
60021     /**
60022      * Add a column (experimental...) - defaults to adding to the end..
60023      * @param {Object} config 
60024     */
60025     addColumn : function(c)
60026     {
60027     
60028         var i = this.config.length;
60029         this.config[i] = c;
60030         
60031         if(typeof c.dataIndex == "undefined"){
60032             c.dataIndex = i;
60033         }
60034         if(typeof c.renderer == "string"){
60035             c.renderer = Roo.util.Format[c.renderer];
60036         }
60037         if(typeof c.id == "undefined"){
60038             c.id = Roo.id();
60039         }
60040         if(c.editor && c.editor.xtype){
60041             c.editor  = Roo.factory(c.editor, Roo.grid);
60042         }
60043         if(c.editor && c.editor.isFormField){
60044             c.editor = new Roo.grid.GridEditor(c.editor);
60045         }
60046         this.lookup[c.id] = c;
60047     }
60048     
60049 });
60050
60051 Roo.grid.ColumnModel.defaultRenderer = function(value)
60052 {
60053     if(typeof value == "object") {
60054         return value;
60055     }
60056         if(typeof value == "string" && value.length < 1){
60057             return "&#160;";
60058         }
60059     
60060         return String.format("{0}", value);
60061 };
60062
60063 // Alias for backwards compatibility
60064 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
60065 /*
60066  * Based on:
60067  * Ext JS Library 1.1.1
60068  * Copyright(c) 2006-2007, Ext JS, LLC.
60069  *
60070  * Originally Released Under LGPL - original licence link has changed is not relivant.
60071  *
60072  * Fork - LGPL
60073  * <script type="text/javascript">
60074  */
60075
60076 /**
60077  * @class Roo.grid.AbstractSelectionModel
60078  * @extends Roo.util.Observable
60079  * @abstract
60080  * Abstract base class for grid SelectionModels.  It provides the interface that should be
60081  * implemented by descendant classes.  This class should not be directly instantiated.
60082  * @constructor
60083  */
60084 Roo.grid.AbstractSelectionModel = function(){
60085     this.locked = false;
60086     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
60087 };
60088
60089 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
60090     /** @ignore Called by the grid automatically. Do not call directly. */
60091     init : function(grid){
60092         this.grid = grid;
60093         this.initEvents();
60094     },
60095
60096     /**
60097      * Locks the selections.
60098      */
60099     lock : function(){
60100         this.locked = true;
60101     },
60102
60103     /**
60104      * Unlocks the selections.
60105      */
60106     unlock : function(){
60107         this.locked = false;
60108     },
60109
60110     /**
60111      * Returns true if the selections are locked.
60112      * @return {Boolean}
60113      */
60114     isLocked : function(){
60115         return this.locked;
60116     }
60117 });/*
60118  * Based on:
60119  * Ext JS Library 1.1.1
60120  * Copyright(c) 2006-2007, Ext JS, LLC.
60121  *
60122  * Originally Released Under LGPL - original licence link has changed is not relivant.
60123  *
60124  * Fork - LGPL
60125  * <script type="text/javascript">
60126  */
60127 /**
60128  * @extends Roo.grid.AbstractSelectionModel
60129  * @class Roo.grid.RowSelectionModel
60130  * The default SelectionModel used by {@link Roo.grid.Grid}.
60131  * It supports multiple selections and keyboard selection/navigation. 
60132  * @constructor
60133  * @param {Object} config
60134  */
60135 Roo.grid.RowSelectionModel = function(config){
60136     Roo.apply(this, config);
60137     this.selections = new Roo.util.MixedCollection(false, function(o){
60138         return o.id;
60139     });
60140
60141     this.last = false;
60142     this.lastActive = false;
60143
60144     this.addEvents({
60145         /**
60146         * @event selectionchange
60147         * Fires when the selection changes
60148         * @param {SelectionModel} this
60149         */
60150        "selectionchange" : true,
60151        /**
60152         * @event afterselectionchange
60153         * Fires after the selection changes (eg. by key press or clicking)
60154         * @param {SelectionModel} this
60155         */
60156        "afterselectionchange" : true,
60157        /**
60158         * @event beforerowselect
60159         * Fires when a row is selected being selected, return false to cancel.
60160         * @param {SelectionModel} this
60161         * @param {Number} rowIndex The selected index
60162         * @param {Boolean} keepExisting False if other selections will be cleared
60163         */
60164        "beforerowselect" : true,
60165        /**
60166         * @event rowselect
60167         * Fires when a row is selected.
60168         * @param {SelectionModel} this
60169         * @param {Number} rowIndex The selected index
60170         * @param {Roo.data.Record} r The record
60171         */
60172        "rowselect" : true,
60173        /**
60174         * @event rowdeselect
60175         * Fires when a row is deselected.
60176         * @param {SelectionModel} this
60177         * @param {Number} rowIndex The selected index
60178         */
60179         "rowdeselect" : true
60180     });
60181     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
60182     this.locked = false;
60183 };
60184
60185 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
60186     /**
60187      * @cfg {Boolean} singleSelect
60188      * True to allow selection of only one row at a time (defaults to false)
60189      */
60190     singleSelect : false,
60191
60192     // private
60193     initEvents : function(){
60194
60195         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
60196             this.grid.on("mousedown", this.handleMouseDown, this);
60197         }else{ // allow click to work like normal
60198             this.grid.on("rowclick", this.handleDragableRowClick, this);
60199         }
60200         // bootstrap does not have a view..
60201         var view = this.grid.view ? this.grid.view : this.grid;
60202         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
60203             "up" : function(e){
60204                 if(!e.shiftKey){
60205                     this.selectPrevious(e.shiftKey);
60206                 }else if(this.last !== false && this.lastActive !== false){
60207                     var last = this.last;
60208                     this.selectRange(this.last,  this.lastActive-1);
60209                     view.focusRow(this.lastActive);
60210                     if(last !== false){
60211                         this.last = last;
60212                     }
60213                 }else{
60214                     this.selectFirstRow();
60215                 }
60216                 this.fireEvent("afterselectionchange", this);
60217             },
60218             "down" : function(e){
60219                 if(!e.shiftKey){
60220                     this.selectNext(e.shiftKey);
60221                 }else if(this.last !== false && this.lastActive !== false){
60222                     var last = this.last;
60223                     this.selectRange(this.last,  this.lastActive+1);
60224                     view.focusRow(this.lastActive);
60225                     if(last !== false){
60226                         this.last = last;
60227                     }
60228                 }else{
60229                     this.selectFirstRow();
60230                 }
60231                 this.fireEvent("afterselectionchange", this);
60232             },
60233             scope: this
60234         });
60235
60236          
60237         view.on("refresh", this.onRefresh, this);
60238         view.on("rowupdated", this.onRowUpdated, this);
60239         view.on("rowremoved", this.onRemove, this);
60240     },
60241
60242     // private
60243     onRefresh : function(){
60244         var ds = this.grid.ds, i, v = this.grid.view;
60245         var s = this.selections;
60246         s.each(function(r){
60247             if((i = ds.indexOfId(r.id)) != -1){
60248                 v.onRowSelect(i);
60249                 s.add(ds.getAt(i)); // updating the selection relate data
60250             }else{
60251                 s.remove(r);
60252             }
60253         });
60254     },
60255
60256     // private
60257     onRemove : function(v, index, r){
60258         this.selections.remove(r);
60259     },
60260
60261     // private
60262     onRowUpdated : function(v, index, r){
60263         if(this.isSelected(r)){
60264             v.onRowSelect(index);
60265         }
60266     },
60267
60268     /**
60269      * Select records.
60270      * @param {Array} records The records to select
60271      * @param {Boolean} keepExisting (optional) True to keep existing selections
60272      */
60273     selectRecords : function(records, keepExisting){
60274         if(!keepExisting){
60275             this.clearSelections();
60276         }
60277         var ds = this.grid.ds;
60278         for(var i = 0, len = records.length; i < len; i++){
60279             this.selectRow(ds.indexOf(records[i]), true);
60280         }
60281     },
60282
60283     /**
60284      * Gets the number of selected rows.
60285      * @return {Number}
60286      */
60287     getCount : function(){
60288         return this.selections.length;
60289     },
60290
60291     /**
60292      * Selects the first row in the grid.
60293      */
60294     selectFirstRow : function(){
60295         this.selectRow(0);
60296     },
60297
60298     /**
60299      * Select the last row.
60300      * @param {Boolean} keepExisting (optional) True to keep existing selections
60301      */
60302     selectLastRow : function(keepExisting){
60303         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
60304     },
60305
60306     /**
60307      * Selects the row immediately following the last selected row.
60308      * @param {Boolean} keepExisting (optional) True to keep existing selections
60309      */
60310     selectNext : function(keepExisting){
60311         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
60312             this.selectRow(this.last+1, keepExisting);
60313             var view = this.grid.view ? this.grid.view : this.grid;
60314             view.focusRow(this.last);
60315         }
60316     },
60317
60318     /**
60319      * Selects the row that precedes the last selected row.
60320      * @param {Boolean} keepExisting (optional) True to keep existing selections
60321      */
60322     selectPrevious : function(keepExisting){
60323         if(this.last){
60324             this.selectRow(this.last-1, keepExisting);
60325             var view = this.grid.view ? this.grid.view : this.grid;
60326             view.focusRow(this.last);
60327         }
60328     },
60329
60330     /**
60331      * Returns the selected records
60332      * @return {Array} Array of selected records
60333      */
60334     getSelections : function(){
60335         return [].concat(this.selections.items);
60336     },
60337
60338     /**
60339      * Returns the first selected record.
60340      * @return {Record}
60341      */
60342     getSelected : function(){
60343         return this.selections.itemAt(0);
60344     },
60345
60346
60347     /**
60348      * Clears all selections.
60349      */
60350     clearSelections : function(fast){
60351         if(this.locked) {
60352             return;
60353         }
60354         if(fast !== true){
60355             var ds = this.grid.ds;
60356             var s = this.selections;
60357             s.each(function(r){
60358                 this.deselectRow(ds.indexOfId(r.id));
60359             }, this);
60360             s.clear();
60361         }else{
60362             this.selections.clear();
60363         }
60364         this.last = false;
60365     },
60366
60367
60368     /**
60369      * Selects all rows.
60370      */
60371     selectAll : function(){
60372         if(this.locked) {
60373             return;
60374         }
60375         this.selections.clear();
60376         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60377             this.selectRow(i, true);
60378         }
60379     },
60380
60381     /**
60382      * Returns True if there is a selection.
60383      * @return {Boolean}
60384      */
60385     hasSelection : function(){
60386         return this.selections.length > 0;
60387     },
60388
60389     /**
60390      * Returns True if the specified row is selected.
60391      * @param {Number/Record} record The record or index of the record to check
60392      * @return {Boolean}
60393      */
60394     isSelected : function(index){
60395         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60396         return (r && this.selections.key(r.id) ? true : false);
60397     },
60398
60399     /**
60400      * Returns True if the specified record id is selected.
60401      * @param {String} id The id of record to check
60402      * @return {Boolean}
60403      */
60404     isIdSelected : function(id){
60405         return (this.selections.key(id) ? true : false);
60406     },
60407
60408     // private
60409     handleMouseDown : function(e, t)
60410     {
60411         var view = this.grid.view ? this.grid.view : this.grid;
60412         var rowIndex;
60413         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60414             return;
60415         };
60416         if(e.shiftKey && this.last !== false){
60417             var last = this.last;
60418             this.selectRange(last, rowIndex, e.ctrlKey);
60419             this.last = last; // reset the last
60420             view.focusRow(rowIndex);
60421         }else{
60422             var isSelected = this.isSelected(rowIndex);
60423             if(e.button !== 0 && isSelected){
60424                 view.focusRow(rowIndex);
60425             }else if(e.ctrlKey && isSelected){
60426                 this.deselectRow(rowIndex);
60427             }else if(!isSelected){
60428                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60429                 view.focusRow(rowIndex);
60430             }
60431         }
60432         this.fireEvent("afterselectionchange", this);
60433     },
60434     // private
60435     handleDragableRowClick :  function(grid, rowIndex, e) 
60436     {
60437         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
60438             this.selectRow(rowIndex, false);
60439             var view = this.grid.view ? this.grid.view : this.grid;
60440             view.focusRow(rowIndex);
60441              this.fireEvent("afterselectionchange", this);
60442         }
60443     },
60444     
60445     /**
60446      * Selects multiple rows.
60447      * @param {Array} rows Array of the indexes of the row to select
60448      * @param {Boolean} keepExisting (optional) True to keep existing selections
60449      */
60450     selectRows : function(rows, keepExisting){
60451         if(!keepExisting){
60452             this.clearSelections();
60453         }
60454         for(var i = 0, len = rows.length; i < len; i++){
60455             this.selectRow(rows[i], true);
60456         }
60457     },
60458
60459     /**
60460      * Selects a range of rows. All rows in between startRow and endRow are also selected.
60461      * @param {Number} startRow The index of the first row in the range
60462      * @param {Number} endRow The index of the last row in the range
60463      * @param {Boolean} keepExisting (optional) True to retain existing selections
60464      */
60465     selectRange : function(startRow, endRow, keepExisting){
60466         if(this.locked) {
60467             return;
60468         }
60469         if(!keepExisting){
60470             this.clearSelections();
60471         }
60472         if(startRow <= endRow){
60473             for(var i = startRow; i <= endRow; i++){
60474                 this.selectRow(i, true);
60475             }
60476         }else{
60477             for(var i = startRow; i >= endRow; i--){
60478                 this.selectRow(i, true);
60479             }
60480         }
60481     },
60482
60483     /**
60484      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
60485      * @param {Number} startRow The index of the first row in the range
60486      * @param {Number} endRow The index of the last row in the range
60487      */
60488     deselectRange : function(startRow, endRow, preventViewNotify){
60489         if(this.locked) {
60490             return;
60491         }
60492         for(var i = startRow; i <= endRow; i++){
60493             this.deselectRow(i, preventViewNotify);
60494         }
60495     },
60496
60497     /**
60498      * Selects a row.
60499      * @param {Number} row The index of the row to select
60500      * @param {Boolean} keepExisting (optional) True to keep existing selections
60501      */
60502     selectRow : function(index, keepExisting, preventViewNotify){
60503         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
60504             return;
60505         }
60506         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
60507             if(!keepExisting || this.singleSelect){
60508                 this.clearSelections();
60509             }
60510             var r = this.grid.ds.getAt(index);
60511             this.selections.add(r);
60512             this.last = this.lastActive = index;
60513             if(!preventViewNotify){
60514                 var view = this.grid.view ? this.grid.view : this.grid;
60515                 view.onRowSelect(index);
60516             }
60517             this.fireEvent("rowselect", this, index, r);
60518             this.fireEvent("selectionchange", this);
60519         }
60520     },
60521
60522     /**
60523      * Deselects a row.
60524      * @param {Number} row The index of the row to deselect
60525      */
60526     deselectRow : function(index, preventViewNotify){
60527         if(this.locked) {
60528             return;
60529         }
60530         if(this.last == index){
60531             this.last = false;
60532         }
60533         if(this.lastActive == index){
60534             this.lastActive = false;
60535         }
60536         var r = this.grid.ds.getAt(index);
60537         this.selections.remove(r);
60538         if(!preventViewNotify){
60539             var view = this.grid.view ? this.grid.view : this.grid;
60540             view.onRowDeselect(index);
60541         }
60542         this.fireEvent("rowdeselect", this, index);
60543         this.fireEvent("selectionchange", this);
60544     },
60545
60546     // private
60547     restoreLast : function(){
60548         if(this._last){
60549             this.last = this._last;
60550         }
60551     },
60552
60553     // private
60554     acceptsNav : function(row, col, cm){
60555         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60556     },
60557
60558     // private
60559     onEditorKey : function(field, e){
60560         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
60561         if(k == e.TAB){
60562             e.stopEvent();
60563             ed.completeEdit();
60564             if(e.shiftKey){
60565                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60566             }else{
60567                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60568             }
60569         }else if(k == e.ENTER && !e.ctrlKey){
60570             e.stopEvent();
60571             ed.completeEdit();
60572             if(e.shiftKey){
60573                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
60574             }else{
60575                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
60576             }
60577         }else if(k == e.ESC){
60578             ed.cancelEdit();
60579         }
60580         if(newCell){
60581             g.startEditing(newCell[0], newCell[1]);
60582         }
60583     }
60584 });/*
60585  * Based on:
60586  * Ext JS Library 1.1.1
60587  * Copyright(c) 2006-2007, Ext JS, LLC.
60588  *
60589  * Originally Released Under LGPL - original licence link has changed is not relivant.
60590  *
60591  * Fork - LGPL
60592  * <script type="text/javascript">
60593  */
60594 /**
60595  * @class Roo.grid.CellSelectionModel
60596  * @extends Roo.grid.AbstractSelectionModel
60597  * This class provides the basic implementation for cell selection in a grid.
60598  * @constructor
60599  * @param {Object} config The object containing the configuration of this model.
60600  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
60601  */
60602 Roo.grid.CellSelectionModel = function(config){
60603     Roo.apply(this, config);
60604
60605     this.selection = null;
60606
60607     this.addEvents({
60608         /**
60609              * @event beforerowselect
60610              * Fires before a cell is selected.
60611              * @param {SelectionModel} this
60612              * @param {Number} rowIndex The selected row index
60613              * @param {Number} colIndex The selected cell index
60614              */
60615             "beforecellselect" : true,
60616         /**
60617              * @event cellselect
60618              * Fires when a cell is selected.
60619              * @param {SelectionModel} this
60620              * @param {Number} rowIndex The selected row index
60621              * @param {Number} colIndex The selected cell index
60622              */
60623             "cellselect" : true,
60624         /**
60625              * @event selectionchange
60626              * Fires when the active selection changes.
60627              * @param {SelectionModel} this
60628              * @param {Object} selection null for no selection or an object (o) with two properties
60629                 <ul>
60630                 <li>o.record: the record object for the row the selection is in</li>
60631                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
60632                 </ul>
60633              */
60634             "selectionchange" : true,
60635         /**
60636              * @event tabend
60637              * Fires when the tab (or enter) was pressed on the last editable cell
60638              * You can use this to trigger add new row.
60639              * @param {SelectionModel} this
60640              */
60641             "tabend" : true,
60642          /**
60643              * @event beforeeditnext
60644              * Fires before the next editable sell is made active
60645              * You can use this to skip to another cell or fire the tabend
60646              *    if you set cell to false
60647              * @param {Object} eventdata object : { cell : [ row, col ] } 
60648              */
60649             "beforeeditnext" : true
60650     });
60651     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
60652 };
60653
60654 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
60655     
60656     enter_is_tab: false,
60657
60658     /** @ignore */
60659     initEvents : function(){
60660         this.grid.on("mousedown", this.handleMouseDown, this);
60661         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
60662         var view = this.grid.view;
60663         view.on("refresh", this.onViewChange, this);
60664         view.on("rowupdated", this.onRowUpdated, this);
60665         view.on("beforerowremoved", this.clearSelections, this);
60666         view.on("beforerowsinserted", this.clearSelections, this);
60667         if(this.grid.isEditor){
60668             this.grid.on("beforeedit", this.beforeEdit,  this);
60669         }
60670     },
60671
60672         //private
60673     beforeEdit : function(e){
60674         this.select(e.row, e.column, false, true, e.record);
60675     },
60676
60677         //private
60678     onRowUpdated : function(v, index, r){
60679         if(this.selection && this.selection.record == r){
60680             v.onCellSelect(index, this.selection.cell[1]);
60681         }
60682     },
60683
60684         //private
60685     onViewChange : function(){
60686         this.clearSelections(true);
60687     },
60688
60689         /**
60690          * Returns the currently selected cell,.
60691          * @return {Array} The selected cell (row, column) or null if none selected.
60692          */
60693     getSelectedCell : function(){
60694         return this.selection ? this.selection.cell : null;
60695     },
60696
60697     /**
60698      * Clears all selections.
60699      * @param {Boolean} true to prevent the gridview from being notified about the change.
60700      */
60701     clearSelections : function(preventNotify){
60702         var s = this.selection;
60703         if(s){
60704             if(preventNotify !== true){
60705                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
60706             }
60707             this.selection = null;
60708             this.fireEvent("selectionchange", this, null);
60709         }
60710     },
60711
60712     /**
60713      * Returns true if there is a selection.
60714      * @return {Boolean}
60715      */
60716     hasSelection : function(){
60717         return this.selection ? true : false;
60718     },
60719
60720     /** @ignore */
60721     handleMouseDown : function(e, t){
60722         var v = this.grid.getView();
60723         if(this.isLocked()){
60724             return;
60725         };
60726         var row = v.findRowIndex(t);
60727         var cell = v.findCellIndex(t);
60728         if(row !== false && cell !== false){
60729             this.select(row, cell);
60730         }
60731     },
60732
60733     /**
60734      * Selects a cell.
60735      * @param {Number} rowIndex
60736      * @param {Number} collIndex
60737      */
60738     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
60739         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
60740             this.clearSelections();
60741             r = r || this.grid.dataSource.getAt(rowIndex);
60742             this.selection = {
60743                 record : r,
60744                 cell : [rowIndex, colIndex]
60745             };
60746             if(!preventViewNotify){
60747                 var v = this.grid.getView();
60748                 v.onCellSelect(rowIndex, colIndex);
60749                 if(preventFocus !== true){
60750                     v.focusCell(rowIndex, colIndex);
60751                 }
60752             }
60753             this.fireEvent("cellselect", this, rowIndex, colIndex);
60754             this.fireEvent("selectionchange", this, this.selection);
60755         }
60756     },
60757
60758         //private
60759     isSelectable : function(rowIndex, colIndex, cm){
60760         return !cm.isHidden(colIndex);
60761     },
60762
60763     /** @ignore */
60764     handleKeyDown : function(e){
60765         //Roo.log('Cell Sel Model handleKeyDown');
60766         if(!e.isNavKeyPress()){
60767             return;
60768         }
60769         var g = this.grid, s = this.selection;
60770         if(!s){
60771             e.stopEvent();
60772             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
60773             if(cell){
60774                 this.select(cell[0], cell[1]);
60775             }
60776             return;
60777         }
60778         var sm = this;
60779         var walk = function(row, col, step){
60780             return g.walkCells(row, col, step, sm.isSelectable,  sm);
60781         };
60782         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
60783         var newCell;
60784
60785       
60786
60787         switch(k){
60788             case e.TAB:
60789                 // handled by onEditorKey
60790                 if (g.isEditor && g.editing) {
60791                     return;
60792                 }
60793                 if(e.shiftKey) {
60794                     newCell = walk(r, c-1, -1);
60795                 } else {
60796                     newCell = walk(r, c+1, 1);
60797                 }
60798                 break;
60799             
60800             case e.DOWN:
60801                newCell = walk(r+1, c, 1);
60802                 break;
60803             
60804             case e.UP:
60805                 newCell = walk(r-1, c, -1);
60806                 break;
60807             
60808             case e.RIGHT:
60809                 newCell = walk(r, c+1, 1);
60810                 break;
60811             
60812             case e.LEFT:
60813                 newCell = walk(r, c-1, -1);
60814                 break;
60815             
60816             case e.ENTER:
60817                 
60818                 if(g.isEditor && !g.editing){
60819                    g.startEditing(r, c);
60820                    e.stopEvent();
60821                    return;
60822                 }
60823                 
60824                 
60825              break;
60826         };
60827         if(newCell){
60828             this.select(newCell[0], newCell[1]);
60829             e.stopEvent();
60830             
60831         }
60832     },
60833
60834     acceptsNav : function(row, col, cm){
60835         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60836     },
60837     /**
60838      * Selects a cell.
60839      * @param {Number} field (not used) - as it's normally used as a listener
60840      * @param {Number} e - event - fake it by using
60841      *
60842      * var e = Roo.EventObjectImpl.prototype;
60843      * e.keyCode = e.TAB
60844      *
60845      * 
60846      */
60847     onEditorKey : function(field, e){
60848         
60849         var k = e.getKey(),
60850             newCell,
60851             g = this.grid,
60852             ed = g.activeEditor,
60853             forward = false;
60854         ///Roo.log('onEditorKey' + k);
60855         
60856         
60857         if (this.enter_is_tab && k == e.ENTER) {
60858             k = e.TAB;
60859         }
60860         
60861         if(k == e.TAB){
60862             if(e.shiftKey){
60863                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60864             }else{
60865                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60866                 forward = true;
60867             }
60868             
60869             e.stopEvent();
60870             
60871         } else if(k == e.ENTER &&  !e.ctrlKey){
60872             ed.completeEdit();
60873             e.stopEvent();
60874             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60875         
60876                 } else if(k == e.ESC){
60877             ed.cancelEdit();
60878         }
60879                 
60880         if (newCell) {
60881             var ecall = { cell : newCell, forward : forward };
60882             this.fireEvent('beforeeditnext', ecall );
60883             newCell = ecall.cell;
60884                         forward = ecall.forward;
60885         }
60886                 
60887         if(newCell){
60888             //Roo.log('next cell after edit');
60889             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
60890         } else if (forward) {
60891             // tabbed past last
60892             this.fireEvent.defer(100, this, ['tabend',this]);
60893         }
60894     }
60895 });/*
60896  * Based on:
60897  * Ext JS Library 1.1.1
60898  * Copyright(c) 2006-2007, Ext JS, LLC.
60899  *
60900  * Originally Released Under LGPL - original licence link has changed is not relivant.
60901  *
60902  * Fork - LGPL
60903  * <script type="text/javascript">
60904  */
60905  
60906 /**
60907  * @class Roo.grid.EditorGrid
60908  * @extends Roo.grid.Grid
60909  * Class for creating and editable grid.
60910  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
60911  * The container MUST have some type of size defined for the grid to fill. The container will be 
60912  * automatically set to position relative if it isn't already.
60913  * @param {Object} dataSource The data model to bind to
60914  * @param {Object} colModel The column model with info about this grid's columns
60915  */
60916 Roo.grid.EditorGrid = function(container, config){
60917     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
60918     this.getGridEl().addClass("xedit-grid");
60919
60920     if(!this.selModel){
60921         this.selModel = new Roo.grid.CellSelectionModel();
60922     }
60923
60924     this.activeEditor = null;
60925
60926         this.addEvents({
60927             /**
60928              * @event beforeedit
60929              * Fires before cell editing is triggered. The edit event object has the following properties <br />
60930              * <ul style="padding:5px;padding-left:16px;">
60931              * <li>grid - This grid</li>
60932              * <li>record - The record being edited</li>
60933              * <li>field - The field name being edited</li>
60934              * <li>value - The value for the field being edited.</li>
60935              * <li>row - The grid row index</li>
60936              * <li>column - The grid column index</li>
60937              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60938              * </ul>
60939              * @param {Object} e An edit event (see above for description)
60940              */
60941             "beforeedit" : true,
60942             /**
60943              * @event afteredit
60944              * Fires after a cell is edited. <br />
60945              * <ul style="padding:5px;padding-left:16px;">
60946              * <li>grid - This grid</li>
60947              * <li>record - The record being edited</li>
60948              * <li>field - The field name being edited</li>
60949              * <li>value - The value being set</li>
60950              * <li>originalValue - The original value for the field, before the edit.</li>
60951              * <li>row - The grid row index</li>
60952              * <li>column - The grid column index</li>
60953              * </ul>
60954              * @param {Object} e An edit event (see above for description)
60955              */
60956             "afteredit" : true,
60957             /**
60958              * @event validateedit
60959              * Fires after a cell is edited, but before the value is set in the record. 
60960          * You can use this to modify the value being set in the field, Return false
60961              * to cancel the change. The edit event object has the following properties <br />
60962              * <ul style="padding:5px;padding-left:16px;">
60963          * <li>editor - This editor</li>
60964              * <li>grid - This grid</li>
60965              * <li>record - The record being edited</li>
60966              * <li>field - The field name being edited</li>
60967              * <li>value - The value being set</li>
60968              * <li>originalValue - The original value for the field, before the edit.</li>
60969              * <li>row - The grid row index</li>
60970              * <li>column - The grid column index</li>
60971              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60972              * </ul>
60973              * @param {Object} e An edit event (see above for description)
60974              */
60975             "validateedit" : true
60976         });
60977     this.on("bodyscroll", this.stopEditing,  this);
60978     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
60979 };
60980
60981 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
60982     /**
60983      * @cfg {Number} clicksToEdit
60984      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
60985      */
60986     clicksToEdit: 2,
60987
60988     // private
60989     isEditor : true,
60990     // private
60991     trackMouseOver: false, // causes very odd FF errors
60992
60993     onCellDblClick : function(g, row, col){
60994         this.startEditing(row, col);
60995     },
60996
60997     onEditComplete : function(ed, value, startValue){
60998         this.editing = false;
60999         this.activeEditor = null;
61000         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
61001         var r = ed.record;
61002         var field = this.colModel.getDataIndex(ed.col);
61003         var e = {
61004             grid: this,
61005             record: r,
61006             field: field,
61007             originalValue: startValue,
61008             value: value,
61009             row: ed.row,
61010             column: ed.col,
61011             cancel:false,
61012             editor: ed
61013         };
61014         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
61015         cell.show();
61016           
61017         if(String(value) !== String(startValue)){
61018             
61019             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
61020                 r.set(field, e.value);
61021                 // if we are dealing with a combo box..
61022                 // then we also set the 'name' colum to be the displayField
61023                 if (ed.field.displayField && ed.field.name) {
61024                     r.set(ed.field.name, ed.field.el.dom.value);
61025                 }
61026                 
61027                 delete e.cancel; //?? why!!!
61028                 this.fireEvent("afteredit", e);
61029             }
61030         } else {
61031             this.fireEvent("afteredit", e); // always fire it!
61032         }
61033         this.view.focusCell(ed.row, ed.col);
61034     },
61035
61036     /**
61037      * Starts editing the specified for the specified row/column
61038      * @param {Number} rowIndex
61039      * @param {Number} colIndex
61040      */
61041     startEditing : function(row, col){
61042         this.stopEditing();
61043         if(this.colModel.isCellEditable(col, row)){
61044             this.view.ensureVisible(row, col, true);
61045           
61046             var r = this.dataSource.getAt(row);
61047             var field = this.colModel.getDataIndex(col);
61048             var cell = Roo.get(this.view.getCell(row,col));
61049             var e = {
61050                 grid: this,
61051                 record: r,
61052                 field: field,
61053                 value: r.data[field],
61054                 row: row,
61055                 column: col,
61056                 cancel:false 
61057             };
61058             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
61059                 this.editing = true;
61060                 var ed = this.colModel.getCellEditor(col, row);
61061                 
61062                 if (!ed) {
61063                     return;
61064                 }
61065                 if(!ed.rendered){
61066                     ed.render(ed.parentEl || document.body);
61067                 }
61068                 ed.field.reset();
61069                
61070                 cell.hide();
61071                 
61072                 (function(){ // complex but required for focus issues in safari, ie and opera
61073                     ed.row = row;
61074                     ed.col = col;
61075                     ed.record = r;
61076                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
61077                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
61078                     this.activeEditor = ed;
61079                     var v = r.data[field];
61080                     ed.startEdit(this.view.getCell(row, col), v);
61081                     // combo's with 'displayField and name set
61082                     if (ed.field.displayField && ed.field.name) {
61083                         ed.field.el.dom.value = r.data[ed.field.name];
61084                     }
61085                     
61086                     
61087                 }).defer(50, this);
61088             }
61089         }
61090     },
61091         
61092     /**
61093      * Stops any active editing
61094      */
61095     stopEditing : function(){
61096         if(this.activeEditor){
61097             this.activeEditor.completeEdit();
61098         }
61099         this.activeEditor = null;
61100     },
61101         
61102          /**
61103      * Called to get grid's drag proxy text, by default returns this.ddText.
61104      * @return {String}
61105      */
61106     getDragDropText : function(){
61107         var count = this.selModel.getSelectedCell() ? 1 : 0;
61108         return String.format(this.ddText, count, count == 1 ? '' : 's');
61109     }
61110         
61111 });/*
61112  * Based on:
61113  * Ext JS Library 1.1.1
61114  * Copyright(c) 2006-2007, Ext JS, LLC.
61115  *
61116  * Originally Released Under LGPL - original licence link has changed is not relivant.
61117  *
61118  * Fork - LGPL
61119  * <script type="text/javascript">
61120  */
61121
61122 // private - not really -- you end up using it !
61123 // This is a support class used internally by the Grid components
61124
61125 /**
61126  * @class Roo.grid.GridEditor
61127  * @extends Roo.Editor
61128  * Class for creating and editable grid elements.
61129  * @param {Object} config any settings (must include field)
61130  */
61131 Roo.grid.GridEditor = function(field, config){
61132     if (!config && field.field) {
61133         config = field;
61134         field = Roo.factory(config.field, Roo.form);
61135     }
61136     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
61137     field.monitorTab = false;
61138 };
61139
61140 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
61141     
61142     /**
61143      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
61144      */
61145     
61146     alignment: "tl-tl",
61147     autoSize: "width",
61148     hideEl : false,
61149     cls: "x-small-editor x-grid-editor",
61150     shim:false,
61151     shadow:"frame"
61152 });/*
61153  * Based on:
61154  * Ext JS Library 1.1.1
61155  * Copyright(c) 2006-2007, Ext JS, LLC.
61156  *
61157  * Originally Released Under LGPL - original licence link has changed is not relivant.
61158  *
61159  * Fork - LGPL
61160  * <script type="text/javascript">
61161  */
61162   
61163
61164   
61165 Roo.grid.PropertyRecord = Roo.data.Record.create([
61166     {name:'name',type:'string'},  'value'
61167 ]);
61168
61169
61170 Roo.grid.PropertyStore = function(grid, source){
61171     this.grid = grid;
61172     this.store = new Roo.data.Store({
61173         recordType : Roo.grid.PropertyRecord
61174     });
61175     this.store.on('update', this.onUpdate,  this);
61176     if(source){
61177         this.setSource(source);
61178     }
61179     Roo.grid.PropertyStore.superclass.constructor.call(this);
61180 };
61181
61182
61183
61184 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
61185     setSource : function(o){
61186         this.source = o;
61187         this.store.removeAll();
61188         var data = [];
61189         for(var k in o){
61190             if(this.isEditableValue(o[k])){
61191                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
61192             }
61193         }
61194         this.store.loadRecords({records: data}, {}, true);
61195     },
61196
61197     onUpdate : function(ds, record, type){
61198         if(type == Roo.data.Record.EDIT){
61199             var v = record.data['value'];
61200             var oldValue = record.modified['value'];
61201             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
61202                 this.source[record.id] = v;
61203                 record.commit();
61204                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
61205             }else{
61206                 record.reject();
61207             }
61208         }
61209     },
61210
61211     getProperty : function(row){
61212        return this.store.getAt(row);
61213     },
61214
61215     isEditableValue: function(val){
61216         if(val && val instanceof Date){
61217             return true;
61218         }else if(typeof val == 'object' || typeof val == 'function'){
61219             return false;
61220         }
61221         return true;
61222     },
61223
61224     setValue : function(prop, value){
61225         this.source[prop] = value;
61226         this.store.getById(prop).set('value', value);
61227     },
61228
61229     getSource : function(){
61230         return this.source;
61231     }
61232 });
61233
61234 Roo.grid.PropertyColumnModel = function(grid, store){
61235     this.grid = grid;
61236     var g = Roo.grid;
61237     g.PropertyColumnModel.superclass.constructor.call(this, [
61238         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
61239         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
61240     ]);
61241     this.store = store;
61242     this.bselect = Roo.DomHelper.append(document.body, {
61243         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
61244             {tag: 'option', value: 'true', html: 'true'},
61245             {tag: 'option', value: 'false', html: 'false'}
61246         ]
61247     });
61248     Roo.id(this.bselect);
61249     var f = Roo.form;
61250     this.editors = {
61251         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
61252         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
61253         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
61254         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
61255         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
61256     };
61257     this.renderCellDelegate = this.renderCell.createDelegate(this);
61258     this.renderPropDelegate = this.renderProp.createDelegate(this);
61259 };
61260
61261 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
61262     
61263     
61264     nameText : 'Name',
61265     valueText : 'Value',
61266     
61267     dateFormat : 'm/j/Y',
61268     
61269     
61270     renderDate : function(dateVal){
61271         return dateVal.dateFormat(this.dateFormat);
61272     },
61273
61274     renderBool : function(bVal){
61275         return bVal ? 'true' : 'false';
61276     },
61277
61278     isCellEditable : function(colIndex, rowIndex){
61279         return colIndex == 1;
61280     },
61281
61282     getRenderer : function(col){
61283         return col == 1 ?
61284             this.renderCellDelegate : this.renderPropDelegate;
61285     },
61286
61287     renderProp : function(v){
61288         return this.getPropertyName(v);
61289     },
61290
61291     renderCell : function(val){
61292         var rv = val;
61293         if(val instanceof Date){
61294             rv = this.renderDate(val);
61295         }else if(typeof val == 'boolean'){
61296             rv = this.renderBool(val);
61297         }
61298         return Roo.util.Format.htmlEncode(rv);
61299     },
61300
61301     getPropertyName : function(name){
61302         var pn = this.grid.propertyNames;
61303         return pn && pn[name] ? pn[name] : name;
61304     },
61305
61306     getCellEditor : function(colIndex, rowIndex){
61307         var p = this.store.getProperty(rowIndex);
61308         var n = p.data['name'], val = p.data['value'];
61309         
61310         if(typeof(this.grid.customEditors[n]) == 'string'){
61311             return this.editors[this.grid.customEditors[n]];
61312         }
61313         if(typeof(this.grid.customEditors[n]) != 'undefined'){
61314             return this.grid.customEditors[n];
61315         }
61316         if(val instanceof Date){
61317             return this.editors['date'];
61318         }else if(typeof val == 'number'){
61319             return this.editors['number'];
61320         }else if(typeof val == 'boolean'){
61321             return this.editors['boolean'];
61322         }else{
61323             return this.editors['string'];
61324         }
61325     }
61326 });
61327
61328 /**
61329  * @class Roo.grid.PropertyGrid
61330  * @extends Roo.grid.EditorGrid
61331  * This class represents the  interface of a component based property grid control.
61332  * <br><br>Usage:<pre><code>
61333  var grid = new Roo.grid.PropertyGrid("my-container-id", {
61334       
61335  });
61336  // set any options
61337  grid.render();
61338  * </code></pre>
61339   
61340  * @constructor
61341  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61342  * The container MUST have some type of size defined for the grid to fill. The container will be
61343  * automatically set to position relative if it isn't already.
61344  * @param {Object} config A config object that sets properties on this grid.
61345  */
61346 Roo.grid.PropertyGrid = function(container, config){
61347     config = config || {};
61348     var store = new Roo.grid.PropertyStore(this);
61349     this.store = store;
61350     var cm = new Roo.grid.PropertyColumnModel(this, store);
61351     store.store.sort('name', 'ASC');
61352     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
61353         ds: store.store,
61354         cm: cm,
61355         enableColLock:false,
61356         enableColumnMove:false,
61357         stripeRows:false,
61358         trackMouseOver: false,
61359         clicksToEdit:1
61360     }, config));
61361     this.getGridEl().addClass('x-props-grid');
61362     this.lastEditRow = null;
61363     this.on('columnresize', this.onColumnResize, this);
61364     this.addEvents({
61365          /**
61366              * @event beforepropertychange
61367              * Fires before a property changes (return false to stop?)
61368              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61369              * @param {String} id Record Id
61370              * @param {String} newval New Value
61371          * @param {String} oldval Old Value
61372              */
61373         "beforepropertychange": true,
61374         /**
61375              * @event propertychange
61376              * Fires after a property changes
61377              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61378              * @param {String} id Record Id
61379              * @param {String} newval New Value
61380          * @param {String} oldval Old Value
61381              */
61382         "propertychange": true
61383     });
61384     this.customEditors = this.customEditors || {};
61385 };
61386 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61387     
61388      /**
61389      * @cfg {Object} customEditors map of colnames=> custom editors.
61390      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61391      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61392      * false disables editing of the field.
61393          */
61394     
61395       /**
61396      * @cfg {Object} propertyNames map of property Names to their displayed value
61397          */
61398     
61399     render : function(){
61400         Roo.grid.PropertyGrid.superclass.render.call(this);
61401         this.autoSize.defer(100, this);
61402     },
61403
61404     autoSize : function(){
61405         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61406         if(this.view){
61407             this.view.fitColumns();
61408         }
61409     },
61410
61411     onColumnResize : function(){
61412         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61413         this.autoSize();
61414     },
61415     /**
61416      * Sets the data for the Grid
61417      * accepts a Key => Value object of all the elements avaiable.
61418      * @param {Object} data  to appear in grid.
61419      */
61420     setSource : function(source){
61421         this.store.setSource(source);
61422         //this.autoSize();
61423     },
61424     /**
61425      * Gets all the data from the grid.
61426      * @return {Object} data  data stored in grid
61427      */
61428     getSource : function(){
61429         return this.store.getSource();
61430     }
61431 });/*
61432   
61433  * Licence LGPL
61434  
61435  */
61436  
61437 /**
61438  * @class Roo.grid.Calendar
61439  * @extends Roo.grid.Grid
61440  * This class extends the Grid to provide a calendar widget
61441  * <br><br>Usage:<pre><code>
61442  var grid = new Roo.grid.Calendar("my-container-id", {
61443      ds: myDataStore,
61444      cm: myColModel,
61445      selModel: mySelectionModel,
61446      autoSizeColumns: true,
61447      monitorWindowResize: false,
61448      trackMouseOver: true
61449      eventstore : real data store..
61450  });
61451  // set any options
61452  grid.render();
61453   
61454   * @constructor
61455  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61456  * The container MUST have some type of size defined for the grid to fill. The container will be
61457  * automatically set to position relative if it isn't already.
61458  * @param {Object} config A config object that sets properties on this grid.
61459  */
61460 Roo.grid.Calendar = function(container, config){
61461         // initialize the container
61462         this.container = Roo.get(container);
61463         this.container.update("");
61464         this.container.setStyle("overflow", "hidden");
61465     this.container.addClass('x-grid-container');
61466
61467     this.id = this.container.id;
61468
61469     Roo.apply(this, config);
61470     // check and correct shorthanded configs
61471     
61472     var rows = [];
61473     var d =1;
61474     for (var r = 0;r < 6;r++) {
61475         
61476         rows[r]=[];
61477         for (var c =0;c < 7;c++) {
61478             rows[r][c]= '';
61479         }
61480     }
61481     if (this.eventStore) {
61482         this.eventStore= Roo.factory(this.eventStore, Roo.data);
61483         this.eventStore.on('load',this.onLoad, this);
61484         this.eventStore.on('beforeload',this.clearEvents, this);
61485          
61486     }
61487     
61488     this.dataSource = new Roo.data.Store({
61489             proxy: new Roo.data.MemoryProxy(rows),
61490             reader: new Roo.data.ArrayReader({}, [
61491                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
61492     });
61493
61494     this.dataSource.load();
61495     this.ds = this.dataSource;
61496     this.ds.xmodule = this.xmodule || false;
61497     
61498     
61499     var cellRender = function(v,x,r)
61500     {
61501         return String.format(
61502             '<div class="fc-day  fc-widget-content"><div>' +
61503                 '<div class="fc-event-container"></div>' +
61504                 '<div class="fc-day-number">{0}</div>'+
61505                 
61506                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
61507             '</div></div>', v);
61508     
61509     }
61510     
61511     
61512     this.colModel = new Roo.grid.ColumnModel( [
61513         {
61514             xtype: 'ColumnModel',
61515             xns: Roo.grid,
61516             dataIndex : 'weekday0',
61517             header : 'Sunday',
61518             renderer : cellRender
61519         },
61520         {
61521             xtype: 'ColumnModel',
61522             xns: Roo.grid,
61523             dataIndex : 'weekday1',
61524             header : 'Monday',
61525             renderer : cellRender
61526         },
61527         {
61528             xtype: 'ColumnModel',
61529             xns: Roo.grid,
61530             dataIndex : 'weekday2',
61531             header : 'Tuesday',
61532             renderer : cellRender
61533         },
61534         {
61535             xtype: 'ColumnModel',
61536             xns: Roo.grid,
61537             dataIndex : 'weekday3',
61538             header : 'Wednesday',
61539             renderer : cellRender
61540         },
61541         {
61542             xtype: 'ColumnModel',
61543             xns: Roo.grid,
61544             dataIndex : 'weekday4',
61545             header : 'Thursday',
61546             renderer : cellRender
61547         },
61548         {
61549             xtype: 'ColumnModel',
61550             xns: Roo.grid,
61551             dataIndex : 'weekday5',
61552             header : 'Friday',
61553             renderer : cellRender
61554         },
61555         {
61556             xtype: 'ColumnModel',
61557             xns: Roo.grid,
61558             dataIndex : 'weekday6',
61559             header : 'Saturday',
61560             renderer : cellRender
61561         }
61562     ]);
61563     this.cm = this.colModel;
61564     this.cm.xmodule = this.xmodule || false;
61565  
61566         
61567           
61568     //this.selModel = new Roo.grid.CellSelectionModel();
61569     //this.sm = this.selModel;
61570     //this.selModel.init(this);
61571     
61572     
61573     if(this.width){
61574         this.container.setWidth(this.width);
61575     }
61576
61577     if(this.height){
61578         this.container.setHeight(this.height);
61579     }
61580     /** @private */
61581         this.addEvents({
61582         // raw events
61583         /**
61584          * @event click
61585          * The raw click event for the entire grid.
61586          * @param {Roo.EventObject} e
61587          */
61588         "click" : true,
61589         /**
61590          * @event dblclick
61591          * The raw dblclick event for the entire grid.
61592          * @param {Roo.EventObject} e
61593          */
61594         "dblclick" : true,
61595         /**
61596          * @event contextmenu
61597          * The raw contextmenu event for the entire grid.
61598          * @param {Roo.EventObject} e
61599          */
61600         "contextmenu" : true,
61601         /**
61602          * @event mousedown
61603          * The raw mousedown event for the entire grid.
61604          * @param {Roo.EventObject} e
61605          */
61606         "mousedown" : true,
61607         /**
61608          * @event mouseup
61609          * The raw mouseup event for the entire grid.
61610          * @param {Roo.EventObject} e
61611          */
61612         "mouseup" : true,
61613         /**
61614          * @event mouseover
61615          * The raw mouseover event for the entire grid.
61616          * @param {Roo.EventObject} e
61617          */
61618         "mouseover" : true,
61619         /**
61620          * @event mouseout
61621          * The raw mouseout event for the entire grid.
61622          * @param {Roo.EventObject} e
61623          */
61624         "mouseout" : true,
61625         /**
61626          * @event keypress
61627          * The raw keypress event for the entire grid.
61628          * @param {Roo.EventObject} e
61629          */
61630         "keypress" : true,
61631         /**
61632          * @event keydown
61633          * The raw keydown event for the entire grid.
61634          * @param {Roo.EventObject} e
61635          */
61636         "keydown" : true,
61637
61638         // custom events
61639
61640         /**
61641          * @event cellclick
61642          * Fires when a cell is clicked
61643          * @param {Grid} this
61644          * @param {Number} rowIndex
61645          * @param {Number} columnIndex
61646          * @param {Roo.EventObject} e
61647          */
61648         "cellclick" : true,
61649         /**
61650          * @event celldblclick
61651          * Fires when a cell is double clicked
61652          * @param {Grid} this
61653          * @param {Number} rowIndex
61654          * @param {Number} columnIndex
61655          * @param {Roo.EventObject} e
61656          */
61657         "celldblclick" : true,
61658         /**
61659          * @event rowclick
61660          * Fires when a row is clicked
61661          * @param {Grid} this
61662          * @param {Number} rowIndex
61663          * @param {Roo.EventObject} e
61664          */
61665         "rowclick" : true,
61666         /**
61667          * @event rowdblclick
61668          * Fires when a row is double clicked
61669          * @param {Grid} this
61670          * @param {Number} rowIndex
61671          * @param {Roo.EventObject} e
61672          */
61673         "rowdblclick" : true,
61674         /**
61675          * @event headerclick
61676          * Fires when a header is clicked
61677          * @param {Grid} this
61678          * @param {Number} columnIndex
61679          * @param {Roo.EventObject} e
61680          */
61681         "headerclick" : true,
61682         /**
61683          * @event headerdblclick
61684          * Fires when a header cell is double clicked
61685          * @param {Grid} this
61686          * @param {Number} columnIndex
61687          * @param {Roo.EventObject} e
61688          */
61689         "headerdblclick" : true,
61690         /**
61691          * @event rowcontextmenu
61692          * Fires when a row is right clicked
61693          * @param {Grid} this
61694          * @param {Number} rowIndex
61695          * @param {Roo.EventObject} e
61696          */
61697         "rowcontextmenu" : true,
61698         /**
61699          * @event cellcontextmenu
61700          * Fires when a cell is right clicked
61701          * @param {Grid} this
61702          * @param {Number} rowIndex
61703          * @param {Number} cellIndex
61704          * @param {Roo.EventObject} e
61705          */
61706          "cellcontextmenu" : true,
61707         /**
61708          * @event headercontextmenu
61709          * Fires when a header is right clicked
61710          * @param {Grid} this
61711          * @param {Number} columnIndex
61712          * @param {Roo.EventObject} e
61713          */
61714         "headercontextmenu" : true,
61715         /**
61716          * @event bodyscroll
61717          * Fires when the body element is scrolled
61718          * @param {Number} scrollLeft
61719          * @param {Number} scrollTop
61720          */
61721         "bodyscroll" : true,
61722         /**
61723          * @event columnresize
61724          * Fires when the user resizes a column
61725          * @param {Number} columnIndex
61726          * @param {Number} newSize
61727          */
61728         "columnresize" : true,
61729         /**
61730          * @event columnmove
61731          * Fires when the user moves a column
61732          * @param {Number} oldIndex
61733          * @param {Number} newIndex
61734          */
61735         "columnmove" : true,
61736         /**
61737          * @event startdrag
61738          * Fires when row(s) start being dragged
61739          * @param {Grid} this
61740          * @param {Roo.GridDD} dd The drag drop object
61741          * @param {event} e The raw browser event
61742          */
61743         "startdrag" : true,
61744         /**
61745          * @event enddrag
61746          * Fires when a drag operation is complete
61747          * @param {Grid} this
61748          * @param {Roo.GridDD} dd The drag drop object
61749          * @param {event} e The raw browser event
61750          */
61751         "enddrag" : true,
61752         /**
61753          * @event dragdrop
61754          * Fires when dragged row(s) are dropped on a valid DD target
61755          * @param {Grid} this
61756          * @param {Roo.GridDD} dd The drag drop object
61757          * @param {String} targetId The target drag drop object
61758          * @param {event} e The raw browser event
61759          */
61760         "dragdrop" : true,
61761         /**
61762          * @event dragover
61763          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61764          * @param {Grid} this
61765          * @param {Roo.GridDD} dd The drag drop object
61766          * @param {String} targetId The target drag drop object
61767          * @param {event} e The raw browser event
61768          */
61769         "dragover" : true,
61770         /**
61771          * @event dragenter
61772          *  Fires when the dragged row(s) first cross another DD target while being dragged
61773          * @param {Grid} this
61774          * @param {Roo.GridDD} dd The drag drop object
61775          * @param {String} targetId The target drag drop object
61776          * @param {event} e The raw browser event
61777          */
61778         "dragenter" : true,
61779         /**
61780          * @event dragout
61781          * Fires when the dragged row(s) leave another DD target while being dragged
61782          * @param {Grid} this
61783          * @param {Roo.GridDD} dd The drag drop object
61784          * @param {String} targetId The target drag drop object
61785          * @param {event} e The raw browser event
61786          */
61787         "dragout" : true,
61788         /**
61789          * @event rowclass
61790          * Fires when a row is rendered, so you can change add a style to it.
61791          * @param {GridView} gridview   The grid view
61792          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61793          */
61794         'rowclass' : true,
61795
61796         /**
61797          * @event render
61798          * Fires when the grid is rendered
61799          * @param {Grid} grid
61800          */
61801         'render' : true,
61802             /**
61803              * @event select
61804              * Fires when a date is selected
61805              * @param {DatePicker} this
61806              * @param {Date} date The selected date
61807              */
61808         'select': true,
61809         /**
61810              * @event monthchange
61811              * Fires when the displayed month changes 
61812              * @param {DatePicker} this
61813              * @param {Date} date The selected month
61814              */
61815         'monthchange': true,
61816         /**
61817              * @event evententer
61818              * Fires when mouse over an event
61819              * @param {Calendar} this
61820              * @param {event} Event
61821              */
61822         'evententer': true,
61823         /**
61824              * @event eventleave
61825              * Fires when the mouse leaves an
61826              * @param {Calendar} this
61827              * @param {event}
61828              */
61829         'eventleave': true,
61830         /**
61831              * @event eventclick
61832              * Fires when the mouse click an
61833              * @param {Calendar} this
61834              * @param {event}
61835              */
61836         'eventclick': true,
61837         /**
61838              * @event eventrender
61839              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
61840              * @param {Calendar} this
61841              * @param {data} data to be modified
61842              */
61843         'eventrender': true
61844         
61845     });
61846
61847     Roo.grid.Grid.superclass.constructor.call(this);
61848     this.on('render', function() {
61849         this.view.el.addClass('x-grid-cal'); 
61850         
61851         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
61852
61853     },this);
61854     
61855     if (!Roo.grid.Calendar.style) {
61856         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
61857             
61858             
61859             '.x-grid-cal .x-grid-col' :  {
61860                 height: 'auto !important',
61861                 'vertical-align': 'top'
61862             },
61863             '.x-grid-cal  .fc-event-hori' : {
61864                 height: '14px'
61865             }
61866              
61867             
61868         }, Roo.id());
61869     }
61870
61871     
61872     
61873 };
61874 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
61875     /**
61876      * @cfg {Store} eventStore The store that loads events.
61877      */
61878     eventStore : 25,
61879
61880      
61881     activeDate : false,
61882     startDay : 0,
61883     autoWidth : true,
61884     monitorWindowResize : false,
61885
61886     
61887     resizeColumns : function() {
61888         var col = (this.view.el.getWidth() / 7) - 3;
61889         // loop through cols, and setWidth
61890         for(var i =0 ; i < 7 ; i++){
61891             this.cm.setColumnWidth(i, col);
61892         }
61893     },
61894      setDate :function(date) {
61895         
61896         Roo.log('setDate?');
61897         
61898         this.resizeColumns();
61899         var vd = this.activeDate;
61900         this.activeDate = date;
61901 //        if(vd && this.el){
61902 //            var t = date.getTime();
61903 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
61904 //                Roo.log('using add remove');
61905 //                
61906 //                this.fireEvent('monthchange', this, date);
61907 //                
61908 //                this.cells.removeClass("fc-state-highlight");
61909 //                this.cells.each(function(c){
61910 //                   if(c.dateValue == t){
61911 //                       c.addClass("fc-state-highlight");
61912 //                       setTimeout(function(){
61913 //                            try{c.dom.firstChild.focus();}catch(e){}
61914 //                       }, 50);
61915 //                       return false;
61916 //                   }
61917 //                   return true;
61918 //                });
61919 //                return;
61920 //            }
61921 //        }
61922         
61923         var days = date.getDaysInMonth();
61924         
61925         var firstOfMonth = date.getFirstDateOfMonth();
61926         var startingPos = firstOfMonth.getDay()-this.startDay;
61927         
61928         if(startingPos < this.startDay){
61929             startingPos += 7;
61930         }
61931         
61932         var pm = date.add(Date.MONTH, -1);
61933         var prevStart = pm.getDaysInMonth()-startingPos;
61934 //        
61935         
61936         
61937         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61938         
61939         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
61940         //this.cells.addClassOnOver('fc-state-hover');
61941         
61942         var cells = this.cells.elements;
61943         var textEls = this.textNodes;
61944         
61945         //Roo.each(cells, function(cell){
61946         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
61947         //});
61948         
61949         days += startingPos;
61950
61951         // convert everything to numbers so it's fast
61952         var day = 86400000;
61953         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
61954         //Roo.log(d);
61955         //Roo.log(pm);
61956         //Roo.log(prevStart);
61957         
61958         var today = new Date().clearTime().getTime();
61959         var sel = date.clearTime().getTime();
61960         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
61961         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
61962         var ddMatch = this.disabledDatesRE;
61963         var ddText = this.disabledDatesText;
61964         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
61965         var ddaysText = this.disabledDaysText;
61966         var format = this.format;
61967         
61968         var setCellClass = function(cal, cell){
61969             
61970             //Roo.log('set Cell Class');
61971             cell.title = "";
61972             var t = d.getTime();
61973             
61974             //Roo.log(d);
61975             
61976             
61977             cell.dateValue = t;
61978             if(t == today){
61979                 cell.className += " fc-today";
61980                 cell.className += " fc-state-highlight";
61981                 cell.title = cal.todayText;
61982             }
61983             if(t == sel){
61984                 // disable highlight in other month..
61985                 cell.className += " fc-state-highlight";
61986                 
61987             }
61988             // disabling
61989             if(t < min) {
61990                 //cell.className = " fc-state-disabled";
61991                 cell.title = cal.minText;
61992                 return;
61993             }
61994             if(t > max) {
61995                 //cell.className = " fc-state-disabled";
61996                 cell.title = cal.maxText;
61997                 return;
61998             }
61999             if(ddays){
62000                 if(ddays.indexOf(d.getDay()) != -1){
62001                     // cell.title = ddaysText;
62002                    // cell.className = " fc-state-disabled";
62003                 }
62004             }
62005             if(ddMatch && format){
62006                 var fvalue = d.dateFormat(format);
62007                 if(ddMatch.test(fvalue)){
62008                     cell.title = ddText.replace("%0", fvalue);
62009                    cell.className = " fc-state-disabled";
62010                 }
62011             }
62012             
62013             if (!cell.initialClassName) {
62014                 cell.initialClassName = cell.dom.className;
62015             }
62016             
62017             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
62018         };
62019
62020         var i = 0;
62021         
62022         for(; i < startingPos; i++) {
62023             cells[i].dayName =  (++prevStart);
62024             Roo.log(textEls[i]);
62025             d.setDate(d.getDate()+1);
62026             
62027             //cells[i].className = "fc-past fc-other-month";
62028             setCellClass(this, cells[i]);
62029         }
62030         
62031         var intDay = 0;
62032         
62033         for(; i < days; i++){
62034             intDay = i - startingPos + 1;
62035             cells[i].dayName =  (intDay);
62036             d.setDate(d.getDate()+1);
62037             
62038             cells[i].className = ''; // "x-date-active";
62039             setCellClass(this, cells[i]);
62040         }
62041         var extraDays = 0;
62042         
62043         for(; i < 42; i++) {
62044             //textEls[i].innerHTML = (++extraDays);
62045             
62046             d.setDate(d.getDate()+1);
62047             cells[i].dayName = (++extraDays);
62048             cells[i].className = "fc-future fc-other-month";
62049             setCellClass(this, cells[i]);
62050         }
62051         
62052         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
62053         
62054         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
62055         
62056         // this will cause all the cells to mis
62057         var rows= [];
62058         var i =0;
62059         for (var r = 0;r < 6;r++) {
62060             for (var c =0;c < 7;c++) {
62061                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
62062             }    
62063         }
62064         
62065         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62066         for(i=0;i<cells.length;i++) {
62067             
62068             this.cells.elements[i].dayName = cells[i].dayName ;
62069             this.cells.elements[i].className = cells[i].className;
62070             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
62071             this.cells.elements[i].title = cells[i].title ;
62072             this.cells.elements[i].dateValue = cells[i].dateValue ;
62073         }
62074         
62075         
62076         
62077         
62078         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
62079         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
62080         
62081         ////if(totalRows != 6){
62082             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
62083            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
62084        // }
62085         
62086         this.fireEvent('monthchange', this, date);
62087         
62088         
62089     },
62090  /**
62091      * Returns the grid's SelectionModel.
62092      * @return {SelectionModel}
62093      */
62094     getSelectionModel : function(){
62095         if(!this.selModel){
62096             this.selModel = new Roo.grid.CellSelectionModel();
62097         }
62098         return this.selModel;
62099     },
62100
62101     load: function() {
62102         this.eventStore.load()
62103         
62104         
62105         
62106     },
62107     
62108     findCell : function(dt) {
62109         dt = dt.clearTime().getTime();
62110         var ret = false;
62111         this.cells.each(function(c){
62112             //Roo.log("check " +c.dateValue + '?=' + dt);
62113             if(c.dateValue == dt){
62114                 ret = c;
62115                 return false;
62116             }
62117             return true;
62118         });
62119         
62120         return ret;
62121     },
62122     
62123     findCells : function(rec) {
62124         var s = rec.data.start_dt.clone().clearTime().getTime();
62125        // Roo.log(s);
62126         var e= rec.data.end_dt.clone().clearTime().getTime();
62127        // Roo.log(e);
62128         var ret = [];
62129         this.cells.each(function(c){
62130              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
62131             
62132             if(c.dateValue > e){
62133                 return ;
62134             }
62135             if(c.dateValue < s){
62136                 return ;
62137             }
62138             ret.push(c);
62139         });
62140         
62141         return ret;    
62142     },
62143     
62144     findBestRow: function(cells)
62145     {
62146         var ret = 0;
62147         
62148         for (var i =0 ; i < cells.length;i++) {
62149             ret  = Math.max(cells[i].rows || 0,ret);
62150         }
62151         return ret;
62152         
62153     },
62154     
62155     
62156     addItem : function(rec)
62157     {
62158         // look for vertical location slot in
62159         var cells = this.findCells(rec);
62160         
62161         rec.row = this.findBestRow(cells);
62162         
62163         // work out the location.
62164         
62165         var crow = false;
62166         var rows = [];
62167         for(var i =0; i < cells.length; i++) {
62168             if (!crow) {
62169                 crow = {
62170                     start : cells[i],
62171                     end :  cells[i]
62172                 };
62173                 continue;
62174             }
62175             if (crow.start.getY() == cells[i].getY()) {
62176                 // on same row.
62177                 crow.end = cells[i];
62178                 continue;
62179             }
62180             // different row.
62181             rows.push(crow);
62182             crow = {
62183                 start: cells[i],
62184                 end : cells[i]
62185             };
62186             
62187         }
62188         
62189         rows.push(crow);
62190         rec.els = [];
62191         rec.rows = rows;
62192         rec.cells = cells;
62193         for (var i = 0; i < cells.length;i++) {
62194             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
62195             
62196         }
62197         
62198         
62199     },
62200     
62201     clearEvents: function() {
62202         
62203         if (!this.eventStore.getCount()) {
62204             return;
62205         }
62206         // reset number of rows in cells.
62207         Roo.each(this.cells.elements, function(c){
62208             c.rows = 0;
62209         });
62210         
62211         this.eventStore.each(function(e) {
62212             this.clearEvent(e);
62213         },this);
62214         
62215     },
62216     
62217     clearEvent : function(ev)
62218     {
62219         if (ev.els) {
62220             Roo.each(ev.els, function(el) {
62221                 el.un('mouseenter' ,this.onEventEnter, this);
62222                 el.un('mouseleave' ,this.onEventLeave, this);
62223                 el.remove();
62224             },this);
62225             ev.els = [];
62226         }
62227     },
62228     
62229     
62230     renderEvent : function(ev,ctr) {
62231         if (!ctr) {
62232              ctr = this.view.el.select('.fc-event-container',true).first();
62233         }
62234         
62235          
62236         this.clearEvent(ev);
62237             //code
62238        
62239         
62240         
62241         ev.els = [];
62242         var cells = ev.cells;
62243         var rows = ev.rows;
62244         this.fireEvent('eventrender', this, ev);
62245         
62246         for(var i =0; i < rows.length; i++) {
62247             
62248             cls = '';
62249             if (i == 0) {
62250                 cls += ' fc-event-start';
62251             }
62252             if ((i+1) == rows.length) {
62253                 cls += ' fc-event-end';
62254             }
62255             
62256             //Roo.log(ev.data);
62257             // how many rows should it span..
62258             var cg = this.eventTmpl.append(ctr,Roo.apply({
62259                 fccls : cls
62260                 
62261             }, ev.data) , true);
62262             
62263             
62264             cg.on('mouseenter' ,this.onEventEnter, this, ev);
62265             cg.on('mouseleave' ,this.onEventLeave, this, ev);
62266             cg.on('click', this.onEventClick, this, ev);
62267             
62268             ev.els.push(cg);
62269             
62270             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
62271             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
62272             //Roo.log(cg);
62273              
62274             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
62275             cg.setWidth(ebox.right - sbox.x -2);
62276         }
62277     },
62278     
62279     renderEvents: function()
62280     {   
62281         // first make sure there is enough space..
62282         
62283         if (!this.eventTmpl) {
62284             this.eventTmpl = new Roo.Template(
62285                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
62286                     '<div class="fc-event-inner">' +
62287                         '<span class="fc-event-time">{time}</span>' +
62288                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
62289                     '</div>' +
62290                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
62291                 '</div>'
62292             );
62293                 
62294         }
62295                
62296         
62297         
62298         this.cells.each(function(c) {
62299             //Roo.log(c.select('.fc-day-content div',true).first());
62300             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
62301         });
62302         
62303         var ctr = this.view.el.select('.fc-event-container',true).first();
62304         
62305         var cls;
62306         this.eventStore.each(function(ev){
62307             
62308             this.renderEvent(ev);
62309              
62310              
62311         }, this);
62312         this.view.layout();
62313         
62314     },
62315     
62316     onEventEnter: function (e, el,event,d) {
62317         this.fireEvent('evententer', this, el, event);
62318     },
62319     
62320     onEventLeave: function (e, el,event,d) {
62321         this.fireEvent('eventleave', this, el, event);
62322     },
62323     
62324     onEventClick: function (e, el,event,d) {
62325         this.fireEvent('eventclick', this, el, event);
62326     },
62327     
62328     onMonthChange: function () {
62329         this.store.load();
62330     },
62331     
62332     onLoad: function () {
62333         
62334         //Roo.log('calendar onload');
62335 //         
62336         if(this.eventStore.getCount() > 0){
62337             
62338            
62339             
62340             this.eventStore.each(function(d){
62341                 
62342                 
62343                 // FIXME..
62344                 var add =   d.data;
62345                 if (typeof(add.end_dt) == 'undefined')  {
62346                     Roo.log("Missing End time in calendar data: ");
62347                     Roo.log(d);
62348                     return;
62349                 }
62350                 if (typeof(add.start_dt) == 'undefined')  {
62351                     Roo.log("Missing Start time in calendar data: ");
62352                     Roo.log(d);
62353                     return;
62354                 }
62355                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
62356                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
62357                 add.id = add.id || d.id;
62358                 add.title = add.title || '??';
62359                 
62360                 this.addItem(d);
62361                 
62362              
62363             },this);
62364         }
62365         
62366         this.renderEvents();
62367     }
62368     
62369
62370 });
62371 /*
62372  grid : {
62373                 xtype: 'Grid',
62374                 xns: Roo.grid,
62375                 listeners : {
62376                     render : function ()
62377                     {
62378                         _this.grid = this;
62379                         
62380                         if (!this.view.el.hasClass('course-timesheet')) {
62381                             this.view.el.addClass('course-timesheet');
62382                         }
62383                         if (this.tsStyle) {
62384                             this.ds.load({});
62385                             return; 
62386                         }
62387                         Roo.log('width');
62388                         Roo.log(_this.grid.view.el.getWidth());
62389                         
62390                         
62391                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62392                             '.course-timesheet .x-grid-row' : {
62393                                 height: '80px'
62394                             },
62395                             '.x-grid-row td' : {
62396                                 'vertical-align' : 0
62397                             },
62398                             '.course-edit-link' : {
62399                                 'color' : 'blue',
62400                                 'text-overflow' : 'ellipsis',
62401                                 'overflow' : 'hidden',
62402                                 'white-space' : 'nowrap',
62403                                 'cursor' : 'pointer'
62404                             },
62405                             '.sub-link' : {
62406                                 'color' : 'green'
62407                             },
62408                             '.de-act-sup-link' : {
62409                                 'color' : 'purple',
62410                                 'text-decoration' : 'line-through'
62411                             },
62412                             '.de-act-link' : {
62413                                 'color' : 'red',
62414                                 'text-decoration' : 'line-through'
62415                             },
62416                             '.course-timesheet .course-highlight' : {
62417                                 'border-top-style': 'dashed !important',
62418                                 'border-bottom-bottom': 'dashed !important'
62419                             },
62420                             '.course-timesheet .course-item' : {
62421                                 'font-family'   : 'tahoma, arial, helvetica',
62422                                 'font-size'     : '11px',
62423                                 'overflow'      : 'hidden',
62424                                 'padding-left'  : '10px',
62425                                 'padding-right' : '10px',
62426                                 'padding-top' : '10px' 
62427                             }
62428                             
62429                         }, Roo.id());
62430                                 this.ds.load({});
62431                     }
62432                 },
62433                 autoWidth : true,
62434                 monitorWindowResize : false,
62435                 cellrenderer : function(v,x,r)
62436                 {
62437                     return v;
62438                 },
62439                 sm : {
62440                     xtype: 'CellSelectionModel',
62441                     xns: Roo.grid
62442                 },
62443                 dataSource : {
62444                     xtype: 'Store',
62445                     xns: Roo.data,
62446                     listeners : {
62447                         beforeload : function (_self, options)
62448                         {
62449                             options.params = options.params || {};
62450                             options.params._month = _this.monthField.getValue();
62451                             options.params.limit = 9999;
62452                             options.params['sort'] = 'when_dt';    
62453                             options.params['dir'] = 'ASC';    
62454                             this.proxy.loadResponse = this.loadResponse;
62455                             Roo.log("load?");
62456                             //this.addColumns();
62457                         },
62458                         load : function (_self, records, options)
62459                         {
62460                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
62461                                 // if you click on the translation.. you can edit it...
62462                                 var el = Roo.get(this);
62463                                 var id = el.dom.getAttribute('data-id');
62464                                 var d = el.dom.getAttribute('data-date');
62465                                 var t = el.dom.getAttribute('data-time');
62466                                 //var id = this.child('span').dom.textContent;
62467                                 
62468                                 //Roo.log(this);
62469                                 Pman.Dialog.CourseCalendar.show({
62470                                     id : id,
62471                                     when_d : d,
62472                                     when_t : t,
62473                                     productitem_active : id ? 1 : 0
62474                                 }, function() {
62475                                     _this.grid.ds.load({});
62476                                 });
62477                            
62478                            });
62479                            
62480                            _this.panel.fireEvent('resize', [ '', '' ]);
62481                         }
62482                     },
62483                     loadResponse : function(o, success, response){
62484                             // this is overridden on before load..
62485                             
62486                             Roo.log("our code?");       
62487                             //Roo.log(success);
62488                             //Roo.log(response)
62489                             delete this.activeRequest;
62490                             if(!success){
62491                                 this.fireEvent("loadexception", this, o, response);
62492                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62493                                 return;
62494                             }
62495                             var result;
62496                             try {
62497                                 result = o.reader.read(response);
62498                             }catch(e){
62499                                 Roo.log("load exception?");
62500                                 this.fireEvent("loadexception", this, o, response, e);
62501                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62502                                 return;
62503                             }
62504                             Roo.log("ready...");        
62505                             // loop through result.records;
62506                             // and set this.tdate[date] = [] << array of records..
62507                             _this.tdata  = {};
62508                             Roo.each(result.records, function(r){
62509                                 //Roo.log(r.data);
62510                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
62511                                     _this.tdata[r.data.when_dt.format('j')] = [];
62512                                 }
62513                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
62514                             });
62515                             
62516                             //Roo.log(_this.tdata);
62517                             
62518                             result.records = [];
62519                             result.totalRecords = 6;
62520                     
62521                             // let's generate some duumy records for the rows.
62522                             //var st = _this.dateField.getValue();
62523                             
62524                             // work out monday..
62525                             //st = st.add(Date.DAY, -1 * st.format('w'));
62526                             
62527                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62528                             
62529                             var firstOfMonth = date.getFirstDayOfMonth();
62530                             var days = date.getDaysInMonth();
62531                             var d = 1;
62532                             var firstAdded = false;
62533                             for (var i = 0; i < result.totalRecords ; i++) {
62534                                 //var d= st.add(Date.DAY, i);
62535                                 var row = {};
62536                                 var added = 0;
62537                                 for(var w = 0 ; w < 7 ; w++){
62538                                     if(!firstAdded && firstOfMonth != w){
62539                                         continue;
62540                                     }
62541                                     if(d > days){
62542                                         continue;
62543                                     }
62544                                     firstAdded = true;
62545                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
62546                                     row['weekday'+w] = String.format(
62547                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
62548                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
62549                                                     d,
62550                                                     date.format('Y-m-')+dd
62551                                                 );
62552                                     added++;
62553                                     if(typeof(_this.tdata[d]) != 'undefined'){
62554                                         Roo.each(_this.tdata[d], function(r){
62555                                             var is_sub = '';
62556                                             var deactive = '';
62557                                             var id = r.id;
62558                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
62559                                             if(r.parent_id*1>0){
62560                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
62561                                                 id = r.parent_id;
62562                                             }
62563                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
62564                                                 deactive = 'de-act-link';
62565                                             }
62566                                             
62567                                             row['weekday'+w] += String.format(
62568                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
62569                                                     id, //0
62570                                                     r.product_id_name, //1
62571                                                     r.when_dt.format('h:ia'), //2
62572                                                     is_sub, //3
62573                                                     deactive, //4
62574                                                     desc // 5
62575                                             );
62576                                         });
62577                                     }
62578                                     d++;
62579                                 }
62580                                 
62581                                 // only do this if something added..
62582                                 if(added > 0){ 
62583                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
62584                                 }
62585                                 
62586                                 
62587                                 // push it twice. (second one with an hour..
62588                                 
62589                             }
62590                             //Roo.log(result);
62591                             this.fireEvent("load", this, o, o.request.arg);
62592                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
62593                         },
62594                     sortInfo : {field: 'when_dt', direction : 'ASC' },
62595                     proxy : {
62596                         xtype: 'HttpProxy',
62597                         xns: Roo.data,
62598                         method : 'GET',
62599                         url : baseURL + '/Roo/Shop_course.php'
62600                     },
62601                     reader : {
62602                         xtype: 'JsonReader',
62603                         xns: Roo.data,
62604                         id : 'id',
62605                         fields : [
62606                             {
62607                                 'name': 'id',
62608                                 'type': 'int'
62609                             },
62610                             {
62611                                 'name': 'when_dt',
62612                                 'type': 'string'
62613                             },
62614                             {
62615                                 'name': 'end_dt',
62616                                 'type': 'string'
62617                             },
62618                             {
62619                                 'name': 'parent_id',
62620                                 'type': 'int'
62621                             },
62622                             {
62623                                 'name': 'product_id',
62624                                 'type': 'int'
62625                             },
62626                             {
62627                                 'name': 'productitem_id',
62628                                 'type': 'int'
62629                             },
62630                             {
62631                                 'name': 'guid',
62632                                 'type': 'int'
62633                             }
62634                         ]
62635                     }
62636                 },
62637                 toolbar : {
62638                     xtype: 'Toolbar',
62639                     xns: Roo,
62640                     items : [
62641                         {
62642                             xtype: 'Button',
62643                             xns: Roo.Toolbar,
62644                             listeners : {
62645                                 click : function (_self, e)
62646                                 {
62647                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62648                                     sd.setMonth(sd.getMonth()-1);
62649                                     _this.monthField.setValue(sd.format('Y-m-d'));
62650                                     _this.grid.ds.load({});
62651                                 }
62652                             },
62653                             text : "Back"
62654                         },
62655                         {
62656                             xtype: 'Separator',
62657                             xns: Roo.Toolbar
62658                         },
62659                         {
62660                             xtype: 'MonthField',
62661                             xns: Roo.form,
62662                             listeners : {
62663                                 render : function (_self)
62664                                 {
62665                                     _this.monthField = _self;
62666                                    // _this.monthField.set  today
62667                                 },
62668                                 select : function (combo, date)
62669                                 {
62670                                     _this.grid.ds.load({});
62671                                 }
62672                             },
62673                             value : (function() { return new Date(); })()
62674                         },
62675                         {
62676                             xtype: 'Separator',
62677                             xns: Roo.Toolbar
62678                         },
62679                         {
62680                             xtype: 'TextItem',
62681                             xns: Roo.Toolbar,
62682                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
62683                         },
62684                         {
62685                             xtype: 'Fill',
62686                             xns: Roo.Toolbar
62687                         },
62688                         {
62689                             xtype: 'Button',
62690                             xns: Roo.Toolbar,
62691                             listeners : {
62692                                 click : function (_self, e)
62693                                 {
62694                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62695                                     sd.setMonth(sd.getMonth()+1);
62696                                     _this.monthField.setValue(sd.format('Y-m-d'));
62697                                     _this.grid.ds.load({});
62698                                 }
62699                             },
62700                             text : "Next"
62701                         }
62702                     ]
62703                 },
62704                  
62705             }
62706         };
62707         
62708         *//*
62709  * Based on:
62710  * Ext JS Library 1.1.1
62711  * Copyright(c) 2006-2007, Ext JS, LLC.
62712  *
62713  * Originally Released Under LGPL - original licence link has changed is not relivant.
62714  *
62715  * Fork - LGPL
62716  * <script type="text/javascript">
62717  */
62718  
62719 /**
62720  * @class Roo.LoadMask
62721  * A simple utility class for generically masking elements while loading data.  If the element being masked has
62722  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
62723  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
62724  * element's UpdateManager load indicator and will be destroyed after the initial load.
62725  * @constructor
62726  * Create a new LoadMask
62727  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
62728  * @param {Object} config The config object
62729  */
62730 Roo.LoadMask = function(el, config){
62731     this.el = Roo.get(el);
62732     Roo.apply(this, config);
62733     if(this.store){
62734         this.store.on('beforeload', this.onBeforeLoad, this);
62735         this.store.on('load', this.onLoad, this);
62736         this.store.on('loadexception', this.onLoadException, this);
62737         this.removeMask = false;
62738     }else{
62739         var um = this.el.getUpdateManager();
62740         um.showLoadIndicator = false; // disable the default indicator
62741         um.on('beforeupdate', this.onBeforeLoad, this);
62742         um.on('update', this.onLoad, this);
62743         um.on('failure', this.onLoad, this);
62744         this.removeMask = true;
62745     }
62746 };
62747
62748 Roo.LoadMask.prototype = {
62749     /**
62750      * @cfg {Boolean} removeMask
62751      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
62752      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
62753      */
62754     removeMask : false,
62755     /**
62756      * @cfg {String} msg
62757      * The text to display in a centered loading message box (defaults to 'Loading...')
62758      */
62759     msg : 'Loading...',
62760     /**
62761      * @cfg {String} msgCls
62762      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
62763      */
62764     msgCls : 'x-mask-loading',
62765
62766     /**
62767      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
62768      * @type Boolean
62769      */
62770     disabled: false,
62771
62772     /**
62773      * Disables the mask to prevent it from being displayed
62774      */
62775     disable : function(){
62776        this.disabled = true;
62777     },
62778
62779     /**
62780      * Enables the mask so that it can be displayed
62781      */
62782     enable : function(){
62783         this.disabled = false;
62784     },
62785     
62786     onLoadException : function()
62787     {
62788         Roo.log(arguments);
62789         
62790         if (typeof(arguments[3]) != 'undefined') {
62791             Roo.MessageBox.alert("Error loading",arguments[3]);
62792         } 
62793         /*
62794         try {
62795             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
62796                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
62797             }   
62798         } catch(e) {
62799             
62800         }
62801         */
62802     
62803         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62804     },
62805     // private
62806     onLoad : function()
62807     {
62808         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62809     },
62810
62811     // private
62812     onBeforeLoad : function(){
62813         if(!this.disabled){
62814             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
62815         }
62816     },
62817
62818     // private
62819     destroy : function(){
62820         if(this.store){
62821             this.store.un('beforeload', this.onBeforeLoad, this);
62822             this.store.un('load', this.onLoad, this);
62823             this.store.un('loadexception', this.onLoadException, this);
62824         }else{
62825             var um = this.el.getUpdateManager();
62826             um.un('beforeupdate', this.onBeforeLoad, this);
62827             um.un('update', this.onLoad, this);
62828             um.un('failure', this.onLoad, this);
62829         }
62830     }
62831 };/*
62832  * Based on:
62833  * Ext JS Library 1.1.1
62834  * Copyright(c) 2006-2007, Ext JS, LLC.
62835  *
62836  * Originally Released Under LGPL - original licence link has changed is not relivant.
62837  *
62838  * Fork - LGPL
62839  * <script type="text/javascript">
62840  */
62841
62842
62843 /**
62844  * @class Roo.XTemplate
62845  * @extends Roo.Template
62846  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
62847 <pre><code>
62848 var t = new Roo.XTemplate(
62849         '&lt;select name="{name}"&gt;',
62850                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
62851         '&lt;/select&gt;'
62852 );
62853  
62854 // then append, applying the master template values
62855  </code></pre>
62856  *
62857  * Supported features:
62858  *
62859  *  Tags:
62860
62861 <pre><code>
62862       {a_variable} - output encoded.
62863       {a_variable.format:("Y-m-d")} - call a method on the variable
62864       {a_variable:raw} - unencoded output
62865       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
62866       {a_variable:this.method_on_template(...)} - call a method on the template object.
62867  
62868 </code></pre>
62869  *  The tpl tag:
62870 <pre><code>
62871         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
62872         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
62873         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
62874         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
62875   
62876         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
62877         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
62878 </code></pre>
62879  *      
62880  */
62881 Roo.XTemplate = function()
62882 {
62883     Roo.XTemplate.superclass.constructor.apply(this, arguments);
62884     if (this.html) {
62885         this.compile();
62886     }
62887 };
62888
62889
62890 Roo.extend(Roo.XTemplate, Roo.Template, {
62891
62892     /**
62893      * The various sub templates
62894      */
62895     tpls : false,
62896     /**
62897      *
62898      * basic tag replacing syntax
62899      * WORD:WORD()
62900      *
62901      * // you can fake an object call by doing this
62902      *  x.t:(test,tesT) 
62903      * 
62904      */
62905     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
62906
62907     /**
62908      * compile the template
62909      *
62910      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
62911      *
62912      */
62913     compile: function()
62914     {
62915         var s = this.html;
62916      
62917         s = ['<tpl>', s, '</tpl>'].join('');
62918     
62919         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
62920             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
62921             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
62922             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
62923             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
62924             m,
62925             id     = 0,
62926             tpls   = [];
62927     
62928         while(true == !!(m = s.match(re))){
62929             var forMatch   = m[0].match(nameRe),
62930                 ifMatch   = m[0].match(ifRe),
62931                 execMatch   = m[0].match(execRe),
62932                 namedMatch   = m[0].match(namedRe),
62933                 
62934                 exp  = null, 
62935                 fn   = null,
62936                 exec = null,
62937                 name = forMatch && forMatch[1] ? forMatch[1] : '';
62938                 
62939             if (ifMatch) {
62940                 // if - puts fn into test..
62941                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
62942                 if(exp){
62943                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
62944                 }
62945             }
62946             
62947             if (execMatch) {
62948                 // exec - calls a function... returns empty if true is  returned.
62949                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
62950                 if(exp){
62951                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
62952                 }
62953             }
62954             
62955             
62956             if (name) {
62957                 // for = 
62958                 switch(name){
62959                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
62960                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
62961                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
62962                 }
62963             }
62964             var uid = namedMatch ? namedMatch[1] : id;
62965             
62966             
62967             tpls.push({
62968                 id:     namedMatch ? namedMatch[1] : id,
62969                 target: name,
62970                 exec:   exec,
62971                 test:   fn,
62972                 body:   m[1] || ''
62973             });
62974             if (namedMatch) {
62975                 s = s.replace(m[0], '');
62976             } else { 
62977                 s = s.replace(m[0], '{xtpl'+ id + '}');
62978             }
62979             ++id;
62980         }
62981         this.tpls = [];
62982         for(var i = tpls.length-1; i >= 0; --i){
62983             this.compileTpl(tpls[i]);
62984             this.tpls[tpls[i].id] = tpls[i];
62985         }
62986         this.master = tpls[tpls.length-1];
62987         return this;
62988     },
62989     /**
62990      * same as applyTemplate, except it's done to one of the subTemplates
62991      * when using named templates, you can do:
62992      *
62993      * var str = pl.applySubTemplate('your-name', values);
62994      *
62995      * 
62996      * @param {Number} id of the template
62997      * @param {Object} values to apply to template
62998      * @param {Object} parent (normaly the instance of this object)
62999      */
63000     applySubTemplate : function(id, values, parent)
63001     {
63002         
63003         
63004         var t = this.tpls[id];
63005         
63006         
63007         try { 
63008             if(t.test && !t.test.call(this, values, parent)){
63009                 return '';
63010             }
63011         } catch(e) {
63012             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
63013             Roo.log(e.toString());
63014             Roo.log(t.test);
63015             return ''
63016         }
63017         try { 
63018             
63019             if(t.exec && t.exec.call(this, values, parent)){
63020                 return '';
63021             }
63022         } catch(e) {
63023             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
63024             Roo.log(e.toString());
63025             Roo.log(t.exec);
63026             return ''
63027         }
63028         try {
63029             var vs = t.target ? t.target.call(this, values, parent) : values;
63030             parent = t.target ? values : parent;
63031             if(t.target && vs instanceof Array){
63032                 var buf = [];
63033                 for(var i = 0, len = vs.length; i < len; i++){
63034                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
63035                 }
63036                 return buf.join('');
63037             }
63038             return t.compiled.call(this, vs, parent);
63039         } catch (e) {
63040             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
63041             Roo.log(e.toString());
63042             Roo.log(t.compiled);
63043             return '';
63044         }
63045     },
63046
63047     compileTpl : function(tpl)
63048     {
63049         var fm = Roo.util.Format;
63050         var useF = this.disableFormats !== true;
63051         var sep = Roo.isGecko ? "+" : ",";
63052         var undef = function(str) {
63053             Roo.log("Property not found :"  + str);
63054             return '';
63055         };
63056         
63057         var fn = function(m, name, format, args)
63058         {
63059             //Roo.log(arguments);
63060             args = args ? args.replace(/\\'/g,"'") : args;
63061             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
63062             if (typeof(format) == 'undefined') {
63063                 format= 'htmlEncode';
63064             }
63065             if (format == 'raw' ) {
63066                 format = false;
63067             }
63068             
63069             if(name.substr(0, 4) == 'xtpl'){
63070                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
63071             }
63072             
63073             // build an array of options to determine if value is undefined..
63074             
63075             // basically get 'xxxx.yyyy' then do
63076             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
63077             //    (function () { Roo.log("Property not found"); return ''; })() :
63078             //    ......
63079             
63080             var udef_ar = [];
63081             var lookfor = '';
63082             Roo.each(name.split('.'), function(st) {
63083                 lookfor += (lookfor.length ? '.': '') + st;
63084                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
63085             });
63086             
63087             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
63088             
63089             
63090             if(format && useF){
63091                 
63092                 args = args ? ',' + args : "";
63093                  
63094                 if(format.substr(0, 5) != "this."){
63095                     format = "fm." + format + '(';
63096                 }else{
63097                     format = 'this.call("'+ format.substr(5) + '", ';
63098                     args = ", values";
63099                 }
63100                 
63101                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
63102             }
63103              
63104             if (args.length) {
63105                 // called with xxyx.yuu:(test,test)
63106                 // change to ()
63107                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
63108             }
63109             // raw.. - :raw modifier..
63110             return "'"+ sep + udef_st  + name + ")"+sep+"'";
63111             
63112         };
63113         var body;
63114         // branched to use + in gecko and [].join() in others
63115         if(Roo.isGecko){
63116             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
63117                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
63118                     "';};};";
63119         }else{
63120             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
63121             body.push(tpl.body.replace(/(\r\n|\n)/g,
63122                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
63123             body.push("'].join('');};};");
63124             body = body.join('');
63125         }
63126         
63127         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
63128        
63129         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
63130         eval(body);
63131         
63132         return this;
63133     },
63134
63135     applyTemplate : function(values){
63136         return this.master.compiled.call(this, values, {});
63137         //var s = this.subs;
63138     },
63139
63140     apply : function(){
63141         return this.applyTemplate.apply(this, arguments);
63142     }
63143
63144  });
63145
63146 Roo.XTemplate.from = function(el){
63147     el = Roo.getDom(el);
63148     return new Roo.XTemplate(el.value || el.innerHTML);
63149 };