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  * Base Class for filtering htmleditor stuff.
44438  *
44439  */
44440
44441 /**
44442  * @class Roo.htmleditor.Filter
44443  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
44444  * @cfg {DomElement} node The node to iterate and filter
44445  * @cfg {boolean|String|Array} tag Tags to replace 
44446  * @constructor
44447  * Create a new Filter.
44448  * @param {Object} config Configuration options
44449  */
44450
44451
44452
44453 Roo.htmleditor.Filter = function(cfg) {
44454     Roo.apply(this.cfg);
44455     // this does not actually call walk as it's really just a abstract class
44456 }
44457
44458
44459 Roo.htmleditor.Filter.prototype = {
44460     
44461     node: false,
44462     
44463     tag: false,
44464
44465     // overrride to do replace comments.
44466     replaceComment : false,
44467     
44468     // overrride to do replace or do stuff with tags..
44469     replaceTag : false,
44470     
44471     walk : function(dom)
44472     {
44473         Roo.each( Array.from(dom.childNodes), function( e ) {
44474             switch(true) {
44475                 
44476                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
44477                     this.replaceComment(e);
44478                     return;
44479                 
44480                 case e.nodeType != 3: //not a node.
44481                     return;
44482                 
44483                 case tag === true: // everything
44484                 case typeof(tag) == 'object' && tag.indexOf(e.tagName) > -1: // array and it matches.
44485                 case typeof(tag) == 'string' && tag == e.tagName: // array and it matches.
44486                     if (this.replaceTag && false === this.replaceTag(e)) {
44487                         return;
44488                     }
44489                     if (e.hasChildNodes()) {
44490                         this.walk(e);
44491                     }
44492                     return;
44493                 
44494                 default:    // tags .. that do not match.
44495                     if (e.hasChildNodes()) {
44496                         this.walk(e);
44497                     }
44498             }
44499             
44500         }, this);
44501         
44502     }
44503 }; 
44504
44505 /**
44506  * @class Roo.htmleditor.FilterAttributes
44507  * clean attributes and  styles including http:// etc.. in attribute
44508  * @constructor
44509 * Run a new Attribute Filter
44510 * @param {Object} config Configuration options
44511  */
44512 Roo.htmleditor.FilterAttributes = function(cfg)
44513 {
44514     Roo.apply(this, cfg);
44515     this.walk(cfg.node);
44516 }
44517
44518 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
44519 {
44520     tag: true, // all tags
44521     
44522     attrib_black : false, // array
44523     attrib_clean : false,
44524     style_white : false,
44525     style_black : false,
44526      
44527      
44528     replaceTag : function(node)
44529     {
44530         if (!node.attributes || !node.attributes.length) {
44531             return true;
44532         }
44533         
44534         for (var i = node.attributes.length-1; i > -1 ; i--) {
44535             var a = node.attributes[i];
44536             //console.log(a);
44537             
44538             if (a.name.toLowerCase().substr(0,2)=='on')  {
44539                 node.removeAttribute(a.name);
44540                 continue;
44541             }
44542             
44543             
44544             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
44545                 node.removeAttribute(a.name);
44546                 continue;
44547             }
44548             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
44549                 this.cleanAttr(node,a.name,a.value); // fixme..
44550                 continue;
44551             }
44552             if (a.name == 'style') {
44553                 this.cleanStyle(node,a.name,a.value);
44554                 continue;
44555             }
44556             /// clean up MS crap..
44557             // tecnically this should be a list of valid class'es..
44558             
44559             
44560             if (a.name == 'class') {
44561                 if (a.value.match(/^Mso/)) {
44562                     node.removeAttribute('class');
44563                 }
44564                 
44565                 if (a.value.match(/^body$/)) {
44566                     node.removeAttribute('class');
44567                 }
44568                 continue;
44569             }
44570             
44571             
44572             // style cleanup!?
44573             // class cleanup?
44574             
44575         }
44576         return true; // clean children
44577     },
44578         
44579     cleanAttr: function(node, n,v)
44580     {
44581         
44582         if (v.match(/^\./) || v.match(/^\//)) {
44583             return;
44584         }
44585         if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44586             return;
44587         }
44588         if (v.match(/^#/)) {
44589             return;
44590         }
44591         if (v.match(/^\{/)) { // allow template editing.
44592             return;
44593         }
44594 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44595         node.removeAttribute(n);
44596         
44597     },
44598     cleanStyle : function(node,  n,v)
44599     {
44600         if (v.match(/expression/)) { //XSS?? should we even bother..
44601             node.removeAttribute(n);
44602             return;
44603         }
44604         
44605         var parts = v.split(/;/);
44606         var clean = [];
44607         
44608         Roo.each(parts, function(p) {
44609             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44610             if (!p.length) {
44611                 return true;
44612             }
44613             var l = p.split(':').shift().replace(/\s+/g,'');
44614             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44615             
44616             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
44617                 return true;
44618             }
44619             //Roo.log()
44620             // only allow 'c whitelisted system attributes'
44621             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
44622                 return true;
44623             }
44624             
44625             
44626             clean.push(p);
44627             return true;
44628         },this);
44629         if (clean.length) { 
44630             node.setAttribute(n, clean.join(';'));
44631         } else {
44632             node.removeAttribute(n);
44633         }
44634         
44635     }
44636         
44637         
44638         
44639     
44640 });/**
44641  * Filter Clean
44642  *
44643  * Based on White / Blacklists etc...
44644  * 
44645  * 
44646  * usually call Roo.apply(Roo.htmleditor.FilterClean)
44647  *
44648  */
44649
44650 /**
44651  * @class Roo.htmleditor.FilterBlack
44652  * remove blacklisted elements.
44653  * @constructor
44654  * Run a new Blacklisted Filter
44655  * @param {Object} config Configuration options
44656  */
44657
44658 Roo.htmleditor.FilterBlack = function(cfg)
44659 {
44660     Roo.apply(this, cfg);
44661     this.walk(cfg.node);
44662 }
44663
44664 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
44665 {
44666     tag : true, // all elements.
44667     /**
44668      * @cfg {array} black blacklist of elements
44669      */
44670     black : false, // array
44671     
44672      
44673     replace : function(n)
44674     {
44675         n.parentNode.removeChild(n);
44676     }
44677 });
44678 /**
44679  * @class Roo.htmleditor.FilterComment
44680  * remove comments.
44681  * @constructor
44682 * Run a new Comments Filter
44683 * @param {Object} config Configuration options
44684  */
44685 Roo.htmleditor.FilterComment = function(cfg)
44686 {
44687     this.walk(cfg.node);
44688 }
44689
44690 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
44691 {
44692   
44693     replaceComment : function(n)
44694     {
44695         n.parentNode.removeChild(n);
44696     }
44697 });/**
44698  * @class Roo.htmleditor.FilterKeepChildren
44699  * remove tags but keep children
44700  * @constructor
44701  * Run a new Keep Children Filter
44702  * @param {Object} config Configuration options
44703  */
44704
44705 Roo.htmleditor.FilterKeepChildren = function(cfg)
44706 {
44707     Roo.apply(this, cfg);
44708     this.walk(cfg.node);
44709 }
44710
44711 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
44712 {
44713     
44714   
44715     replaceTag : function(n)
44716     {
44717         // walk children...
44718         for (var i = 0; i < node.childNodes.length; i++) {
44719             node.removeChild(node.childNodes[i]);
44720             // what if we need to walk these???
44721             node.insertBefore(node.childNodes[i], node);
44722             this.walk(node.childNodes[i]);
44723         }
44724         n.parentNode.removeChild(n);
44725         return false; // don't walk children
44726         
44727         
44728     }
44729 });/**
44730  * @class Roo.htmleditor.FilterParagraph
44731  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
44732  * like on 'push' to remove the <p> tags and replace them with line breaks.
44733  * @constructor
44734  * Run a new Paragraph Filter
44735  * @param {Object} config Configuration options
44736  */
44737
44738 Roo.htmleditor.FilterParagraph = function(cfg)
44739 {
44740     // no need to apply config.
44741     this.walk(cfg.node);
44742 }
44743
44744 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
44745 {
44746     
44747      
44748     tag : 'P',
44749     
44750      
44751     replaceTag : function(node)
44752     {
44753         
44754         if (node.childNodes.length == 1 &&
44755             node.childNodes[0].nodeType == 3 &&
44756             node.childNodes[0].nodeType.textContent.trim().length < 1
44757             ) {
44758             // remove and replace with '<BR>';
44759             node.parentNode.replaceChild(node.documentOwner.createElement('BR'),node);
44760             return false; // no need to walk..
44761         }
44762         
44763         for (var i = 0; i < node.childNodes.length; i++) {
44764             node.removeChild(node.childNodes[i]);
44765             // what if we need to walk these???
44766             node.insertBefore(node.childNodes[i], node);
44767         }
44768         // now what about this?
44769         // <p> &nbsp; </p>
44770         
44771         // double BR.
44772         node.insertBefore(node.documentOwner.createElement('BR'), node);
44773         node.parentNode.removeChild(node);
44774         
44775         return false;
44776
44777     }
44778     
44779     
44780     
44781 };/**
44782  * @class Roo.htmleditor.FilterSpan
44783  * filter span's with no attributes out..
44784  * @constructor
44785  * Run a new Span Filter
44786  * @param {Object} config Configuration options
44787  */
44788
44789 Roo.htmleditor.FilterSpan = function(cfg)
44790 {
44791     // no need to apply config.
44792     this.walk(cfg.node);
44793 }
44794
44795 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.Filter,
44796 {
44797      
44798     tag : 'SPAN',
44799      
44800  
44801     replaceTag : function(node)
44802     {
44803         if (node.attributes && node.attributes.length > 0) {
44804             this.walk(node);
44805             return true;
44806         }
44807         for (var i = 0; i < node.childNodes.length; i++) {
44808             node.removeChild(node.childNodes[i]);
44809             // what if we need to walk these???
44810             node.insertBefore(node.childNodes[i], node);
44811             this.walk(node.childNodes[i]);
44812         }
44813         n.parentNode.removeChild(n);
44814         return false; // don't walk children
44815      
44816     }
44817     
44818 });/**
44819  * @class Roo.htmleditor.FilterTableWidth
44820   try and remove table width data - as that frequently messes up other stuff.
44821  * 
44822  *      was cleanTableWidths.
44823  *
44824  * Quite often pasting from word etc.. results in tables with column and widths.
44825  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44826  *
44827  * @constructor
44828  * Run a new Table Filter
44829  * @param {Object} config Configuration options
44830  */
44831
44832 Roo.htmleditor.FilterTableWidth = function(cfg)
44833 {
44834     // no need to apply config.
44835     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
44836     this.walk(cfg.node);
44837 }
44838
44839 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
44840 {
44841      
44842      
44843     
44844     replaceTag: function(node) {
44845         
44846         
44847       
44848         if (node.hasAttribute('width')) {
44849             node.removeAttribute('width');
44850         }
44851         
44852          
44853         if (node.hasAttribute("style")) {
44854             // pretty basic...
44855             
44856             var styles = node.getAttribute("style").split(";");
44857             var nstyle = [];
44858             Roo.each(styles, function(s) {
44859                 if (!s.match(/:/)) {
44860                     return;
44861                 }
44862                 var kv = s.split(":");
44863                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44864                     return;
44865                 }
44866                 // what ever is left... we allow.
44867                 nstyle.push(s);
44868             });
44869             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44870             if (!nstyle.length) {
44871                 node.removeAttribute('style');
44872             }
44873         }
44874         
44875         this.walk(node);
44876     }
44877 });/**
44878  * @class Roo.htmleditor.FilterWord
44879  * try and clean up all the mess that Word generates.
44880  * 
44881  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
44882  
44883  * @constructor
44884  * Run a new Span Filter
44885  * @param {Object} config Configuration options
44886  */
44887
44888 Roo.htmleditor.FilterWord = function(cfg)
44889 {
44890     // no need to apply config.
44891     this.walk(cfg.node);
44892 }
44893
44894 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
44895 {
44896     tag: true,
44897      
44898     
44899     /**
44900      * Clean up MS wordisms...
44901      */
44902     replaceTag : function(node)
44903     {
44904          
44905         // no idea what this does - span with text, replaceds with just text.
44906         if(
44907                 node.nodeName == 'SPAN' &&
44908                 !node.hasAttributes() &&
44909                 node.childNodes.length == 1 &&
44910                 node.firstChild.nodeName == "#text"  
44911         ) {
44912             var textNode = node.firstChild;
44913             node.removeChild(textNode);
44914             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44915                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44916             }
44917             node.parentNode.insertBefore(textNode, node);
44918             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44919                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44920             }
44921             
44922             node.parentNode.removeChild(node);
44923             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
44924         }
44925         
44926    
44927         
44928         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44929             node.parentNode.removeChild(node);
44930             return false; // dont do chidlren
44931         }
44932         //Roo.log(node.tagName);
44933         // remove - but keep children..
44934         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44935             //Roo.log('-- removed');
44936             while (node.childNodes.length) {
44937                 var cn = node.childNodes[0];
44938                 node.removeChild(cn);
44939                 node.parentNode.insertBefore(cn, node);
44940                 // move node to parent - and clean it..
44941                 this.replaceTag(cn);
44942             }
44943             node.parentNode.removeChild(node);
44944             /// no need to iterate chidlren = it's got none..
44945             //this.iterateChildren(node, this.cleanWord);
44946             return false; // no need to iterate children.
44947         }
44948         // clean styles
44949         if (node.className.length) {
44950             
44951             var cn = node.className.split(/\W+/);
44952             var cna = [];
44953             Roo.each(cn, function(cls) {
44954                 if (cls.match(/Mso[a-zA-Z]+/)) {
44955                     return;
44956                 }
44957                 cna.push(cls);
44958             });
44959             node.className = cna.length ? cna.join(' ') : '';
44960             if (!cna.length) {
44961                 node.removeAttribute("class");
44962             }
44963         }
44964         
44965         if (node.hasAttribute("lang")) {
44966             node.removeAttribute("lang");
44967         }
44968         
44969         if (node.hasAttribute("style")) {
44970             
44971             var styles = node.getAttribute("style").split(";");
44972             var nstyle = [];
44973             Roo.each(styles, function(s) {
44974                 if (!s.match(/:/)) {
44975                     return;
44976                 }
44977                 var kv = s.split(":");
44978                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44979                     return;
44980                 }
44981                 // what ever is left... we allow.
44982                 nstyle.push(s);
44983             });
44984             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44985             if (!nstyle.length) {
44986                 node.removeAttribute('style');
44987             }
44988         }
44989         return true; // do children
44990         
44991         
44992         
44993     }
44994 });//<script type="text/javascript">
44995
44996 /*
44997  * Based  Ext JS Library 1.1.1
44998  * Copyright(c) 2006-2007, Ext JS, LLC.
44999  * LGPL
45000  *
45001  */
45002  
45003 /**
45004  * @class Roo.HtmlEditorCore
45005  * @extends Roo.Component
45006  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
45007  *
45008  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45009  */
45010
45011 Roo.HtmlEditorCore = function(config){
45012     
45013     
45014     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
45015     
45016     
45017     this.addEvents({
45018         /**
45019          * @event initialize
45020          * Fires when the editor is fully initialized (including the iframe)
45021          * @param {Roo.HtmlEditorCore} this
45022          */
45023         initialize: true,
45024         /**
45025          * @event activate
45026          * Fires when the editor is first receives the focus. Any insertion must wait
45027          * until after this event.
45028          * @param {Roo.HtmlEditorCore} this
45029          */
45030         activate: true,
45031          /**
45032          * @event beforesync
45033          * Fires before the textarea is updated with content from the editor iframe. Return false
45034          * to cancel the sync.
45035          * @param {Roo.HtmlEditorCore} this
45036          * @param {String} html
45037          */
45038         beforesync: true,
45039          /**
45040          * @event beforepush
45041          * Fires before the iframe editor is updated with content from the textarea. Return false
45042          * to cancel the push.
45043          * @param {Roo.HtmlEditorCore} this
45044          * @param {String} html
45045          */
45046         beforepush: true,
45047          /**
45048          * @event sync
45049          * Fires when the textarea is updated with content from the editor iframe.
45050          * @param {Roo.HtmlEditorCore} this
45051          * @param {String} html
45052          */
45053         sync: true,
45054          /**
45055          * @event push
45056          * Fires when the iframe editor is updated with content from the textarea.
45057          * @param {Roo.HtmlEditorCore} this
45058          * @param {String} html
45059          */
45060         push: true,
45061         
45062         /**
45063          * @event editorevent
45064          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45065          * @param {Roo.HtmlEditorCore} this
45066          */
45067         editorevent: true
45068         
45069     });
45070     
45071     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
45072     
45073     // defaults : white / black...
45074     this.applyBlacklists();
45075     
45076     
45077     
45078 };
45079
45080
45081 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
45082
45083
45084      /**
45085      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
45086      */
45087     
45088     owner : false,
45089     
45090      /**
45091      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45092      *                        Roo.resizable.
45093      */
45094     resizable : false,
45095      /**
45096      * @cfg {Number} height (in pixels)
45097      */   
45098     height: 300,
45099    /**
45100      * @cfg {Number} width (in pixels)
45101      */   
45102     width: 500,
45103     
45104     /**
45105      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45106      * 
45107      */
45108     stylesheets: false,
45109     
45110     /**
45111      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45112      */
45113     allowComments: false,
45114     // id of frame..
45115     frameId: false,
45116     
45117     // private properties
45118     validationEvent : false,
45119     deferHeight: true,
45120     initialized : false,
45121     activated : false,
45122     sourceEditMode : false,
45123     onFocus : Roo.emptyFn,
45124     iframePad:3,
45125     hideMode:'offsets',
45126     
45127     clearUp: true,
45128     
45129     // blacklist + whitelisted elements..
45130     black: false,
45131     white: false,
45132      
45133     bodyCls : '',
45134
45135     /**
45136      * Protected method that will not generally be called directly. It
45137      * is called when the editor initializes the iframe with HTML contents. Override this method if you
45138      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
45139      */
45140     getDocMarkup : function(){
45141         // body styles..
45142         var st = '';
45143         
45144         // inherit styels from page...?? 
45145         if (this.stylesheets === false) {
45146             
45147             Roo.get(document.head).select('style').each(function(node) {
45148                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45149             });
45150             
45151             Roo.get(document.head).select('link').each(function(node) { 
45152                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45153             });
45154             
45155         } else if (!this.stylesheets.length) {
45156                 // simple..
45157                 st = '<style type="text/css">' +
45158                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45159                    '</style>';
45160         } else {
45161             for (var i in this.stylesheets) {
45162                 if (typeof(this.stylesheets[i]) != 'string') {
45163                     continue;
45164                 }
45165                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
45166             }
45167             
45168         }
45169         
45170         st +=  '<style type="text/css">' +
45171             'IMG { cursor: pointer } ' +
45172         '</style>';
45173
45174         var cls = 'roo-htmleditor-body';
45175         
45176         if(this.bodyCls.length){
45177             cls += ' ' + this.bodyCls;
45178         }
45179         
45180         return '<html><head>' + st  +
45181             //<style type="text/css">' +
45182             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45183             //'</style>' +
45184             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
45185     },
45186
45187     // private
45188     onRender : function(ct, position)
45189     {
45190         var _t = this;
45191         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
45192         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
45193         
45194         
45195         this.el.dom.style.border = '0 none';
45196         this.el.dom.setAttribute('tabIndex', -1);
45197         this.el.addClass('x-hidden hide');
45198         
45199         
45200         
45201         if(Roo.isIE){ // fix IE 1px bogus margin
45202             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
45203         }
45204        
45205         
45206         this.frameId = Roo.id();
45207         
45208          
45209         
45210         var iframe = this.owner.wrap.createChild({
45211             tag: 'iframe',
45212             cls: 'form-control', // bootstrap..
45213             id: this.frameId,
45214             name: this.frameId,
45215             frameBorder : 'no',
45216             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
45217         }, this.el
45218         );
45219         
45220         
45221         this.iframe = iframe.dom;
45222
45223          this.assignDocWin();
45224         
45225         this.doc.designMode = 'on';
45226        
45227         this.doc.open();
45228         this.doc.write(this.getDocMarkup());
45229         this.doc.close();
45230
45231         
45232         var task = { // must defer to wait for browser to be ready
45233             run : function(){
45234                 //console.log("run task?" + this.doc.readyState);
45235                 this.assignDocWin();
45236                 if(this.doc.body || this.doc.readyState == 'complete'){
45237                     try {
45238                         this.doc.designMode="on";
45239                     } catch (e) {
45240                         return;
45241                     }
45242                     Roo.TaskMgr.stop(task);
45243                     this.initEditor.defer(10, this);
45244                 }
45245             },
45246             interval : 10,
45247             duration: 10000,
45248             scope: this
45249         };
45250         Roo.TaskMgr.start(task);
45251
45252     },
45253
45254     // private
45255     onResize : function(w, h)
45256     {
45257          Roo.log('resize: ' +w + ',' + h );
45258         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
45259         if(!this.iframe){
45260             return;
45261         }
45262         if(typeof w == 'number'){
45263             
45264             this.iframe.style.width = w + 'px';
45265         }
45266         if(typeof h == 'number'){
45267             
45268             this.iframe.style.height = h + 'px';
45269             if(this.doc){
45270                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
45271             }
45272         }
45273         
45274     },
45275
45276     /**
45277      * Toggles the editor between standard and source edit mode.
45278      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45279      */
45280     toggleSourceEdit : function(sourceEditMode){
45281         
45282         this.sourceEditMode = sourceEditMode === true;
45283         
45284         if(this.sourceEditMode){
45285  
45286             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
45287             
45288         }else{
45289             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
45290             //this.iframe.className = '';
45291             this.deferFocus();
45292         }
45293         //this.setSize(this.owner.wrap.getSize());
45294         //this.fireEvent('editmodechange', this, this.sourceEditMode);
45295     },
45296
45297     
45298   
45299
45300     /**
45301      * Protected method that will not generally be called directly. If you need/want
45302      * custom HTML cleanup, this is the method you should override.
45303      * @param {String} html The HTML to be cleaned
45304      * return {String} The cleaned HTML
45305      */
45306     cleanHtml : function(html){
45307         html = String(html);
45308         if(html.length > 5){
45309             if(Roo.isSafari){ // strip safari nonsense
45310                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
45311             }
45312         }
45313         if(html == '&nbsp;'){
45314             html = '';
45315         }
45316         return html;
45317     },
45318
45319     /**
45320      * HTML Editor -> Textarea
45321      * Protected method that will not generally be called directly. Syncs the contents
45322      * of the editor iframe with the textarea.
45323      */
45324     syncValue : function(){
45325         if(this.initialized){
45326             var bd = (this.doc.body || this.doc.documentElement);
45327             //this.cleanUpPaste(); -- this is done else where and causes havoc..
45328             var html = bd.innerHTML;
45329             if(Roo.isSafari){
45330                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
45331                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
45332                 if(m && m[1]){
45333                     html = '<div style="'+m[0]+'">' + html + '</div>';
45334                 }
45335             }
45336             html = this.cleanHtml(html);
45337             // fix up the special chars.. normaly like back quotes in word...
45338             // however we do not want to do this with chinese..
45339             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
45340                 
45341                 var cc = match.charCodeAt();
45342
45343                 // Get the character value, handling surrogate pairs
45344                 if (match.length == 2) {
45345                     // It's a surrogate pair, calculate the Unicode code point
45346                     var high = match.charCodeAt(0) - 0xD800;
45347                     var low  = match.charCodeAt(1) - 0xDC00;
45348                     cc = (high * 0x400) + low + 0x10000;
45349                 }  else if (
45350                     (cc >= 0x4E00 && cc < 0xA000 ) ||
45351                     (cc >= 0x3400 && cc < 0x4E00 ) ||
45352                     (cc >= 0xf900 && cc < 0xfb00 )
45353                 ) {
45354                         return match;
45355                 }  
45356          
45357                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
45358                 return "&#" + cc + ";";
45359                 
45360                 
45361             });
45362             
45363             
45364              
45365             if(this.owner.fireEvent('beforesync', this, html) !== false){
45366                 this.el.dom.value = html;
45367                 this.owner.fireEvent('sync', this, html);
45368             }
45369         }
45370     },
45371
45372     /**
45373      * Protected method that will not generally be called directly. Pushes the value of the textarea
45374      * into the iframe editor.
45375      */
45376     pushValue : function(){
45377         if(this.initialized){
45378             var v = this.el.dom.value.trim();
45379             
45380 //            if(v.length < 1){
45381 //                v = '&#160;';
45382 //            }
45383             
45384             if(this.owner.fireEvent('beforepush', this, v) !== false){
45385                 var d = (this.doc.body || this.doc.documentElement);
45386                 d.innerHTML = v;
45387                 this.cleanUpPaste();
45388                 this.el.dom.value = d.innerHTML;
45389                 this.owner.fireEvent('push', this, v);
45390             }
45391         }
45392     },
45393
45394     // private
45395     deferFocus : function(){
45396         this.focus.defer(10, this);
45397     },
45398
45399     // doc'ed in Field
45400     focus : function(){
45401         if(this.win && !this.sourceEditMode){
45402             this.win.focus();
45403         }else{
45404             this.el.focus();
45405         }
45406     },
45407     
45408     assignDocWin: function()
45409     {
45410         var iframe = this.iframe;
45411         
45412          if(Roo.isIE){
45413             this.doc = iframe.contentWindow.document;
45414             this.win = iframe.contentWindow;
45415         } else {
45416 //            if (!Roo.get(this.frameId)) {
45417 //                return;
45418 //            }
45419 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45420 //            this.win = Roo.get(this.frameId).dom.contentWindow;
45421             
45422             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
45423                 return;
45424             }
45425             
45426             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45427             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
45428         }
45429     },
45430     
45431     // private
45432     initEditor : function(){
45433         //console.log("INIT EDITOR");
45434         this.assignDocWin();
45435         
45436         
45437         
45438         this.doc.designMode="on";
45439         this.doc.open();
45440         this.doc.write(this.getDocMarkup());
45441         this.doc.close();
45442         
45443         var dbody = (this.doc.body || this.doc.documentElement);
45444         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
45445         // this copies styles from the containing element into thsi one..
45446         // not sure why we need all of this..
45447         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
45448         
45449         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
45450         //ss['background-attachment'] = 'fixed'; // w3c
45451         dbody.bgProperties = 'fixed'; // ie
45452         //Roo.DomHelper.applyStyles(dbody, ss);
45453         Roo.EventManager.on(this.doc, {
45454             //'mousedown': this.onEditorEvent,
45455             'mouseup': this.onEditorEvent,
45456             'dblclick': this.onEditorEvent,
45457             'click': this.onEditorEvent,
45458             'keyup': this.onEditorEvent,
45459             buffer:100,
45460             scope: this
45461         });
45462         if(Roo.isGecko){
45463             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
45464         }
45465         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
45466             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
45467         }
45468         this.initialized = true;
45469
45470         this.owner.fireEvent('initialize', this);
45471         this.pushValue();
45472     },
45473
45474     // private
45475     onDestroy : function(){
45476         
45477         
45478         
45479         if(this.rendered){
45480             
45481             //for (var i =0; i < this.toolbars.length;i++) {
45482             //    // fixme - ask toolbars for heights?
45483             //    this.toolbars[i].onDestroy();
45484            // }
45485             
45486             //this.wrap.dom.innerHTML = '';
45487             //this.wrap.remove();
45488         }
45489     },
45490
45491     // private
45492     onFirstFocus : function(){
45493         
45494         this.assignDocWin();
45495         
45496         
45497         this.activated = true;
45498          
45499     
45500         if(Roo.isGecko){ // prevent silly gecko errors
45501             this.win.focus();
45502             var s = this.win.getSelection();
45503             if(!s.focusNode || s.focusNode.nodeType != 3){
45504                 var r = s.getRangeAt(0);
45505                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
45506                 r.collapse(true);
45507                 this.deferFocus();
45508             }
45509             try{
45510                 this.execCmd('useCSS', true);
45511                 this.execCmd('styleWithCSS', false);
45512             }catch(e){}
45513         }
45514         this.owner.fireEvent('activate', this);
45515     },
45516
45517     // private
45518     adjustFont: function(btn){
45519         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
45520         //if(Roo.isSafari){ // safari
45521         //    adjust *= 2;
45522        // }
45523         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
45524         if(Roo.isSafari){ // safari
45525             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
45526             v =  (v < 10) ? 10 : v;
45527             v =  (v > 48) ? 48 : v;
45528             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
45529             
45530         }
45531         
45532         
45533         v = Math.max(1, v+adjust);
45534         
45535         this.execCmd('FontSize', v  );
45536     },
45537
45538     onEditorEvent : function(e)
45539     {
45540         this.owner.fireEvent('editorevent', this, e);
45541       //  this.updateToolbar();
45542         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
45543     },
45544
45545     insertTag : function(tg)
45546     {
45547         // could be a bit smarter... -> wrap the current selected tRoo..
45548         if (tg.toLowerCase() == 'span' ||
45549             tg.toLowerCase() == 'code' ||
45550             tg.toLowerCase() == 'sup' ||
45551             tg.toLowerCase() == 'sub' 
45552             ) {
45553             
45554             range = this.createRange(this.getSelection());
45555             var wrappingNode = this.doc.createElement(tg.toLowerCase());
45556             wrappingNode.appendChild(range.extractContents());
45557             range.insertNode(wrappingNode);
45558
45559             return;
45560             
45561             
45562             
45563         }
45564         this.execCmd("formatblock",   tg);
45565         
45566     },
45567     
45568     insertText : function(txt)
45569     {
45570         
45571         
45572         var range = this.createRange();
45573         range.deleteContents();
45574                //alert(Sender.getAttribute('label'));
45575                
45576         range.insertNode(this.doc.createTextNode(txt));
45577     } ,
45578     
45579      
45580
45581     /**
45582      * Executes a Midas editor command on the editor document and performs necessary focus and
45583      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
45584      * @param {String} cmd The Midas command
45585      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45586      */
45587     relayCmd : function(cmd, value){
45588         this.win.focus();
45589         this.execCmd(cmd, value);
45590         this.owner.fireEvent('editorevent', this);
45591         //this.updateToolbar();
45592         this.owner.deferFocus();
45593     },
45594
45595     /**
45596      * Executes a Midas editor command directly on the editor document.
45597      * For visual commands, you should use {@link #relayCmd} instead.
45598      * <b>This should only be called after the editor is initialized.</b>
45599      * @param {String} cmd The Midas command
45600      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45601      */
45602     execCmd : function(cmd, value){
45603         this.doc.execCommand(cmd, false, value === undefined ? null : value);
45604         this.syncValue();
45605     },
45606  
45607  
45608    
45609     /**
45610      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
45611      * to insert tRoo.
45612      * @param {String} text | dom node.. 
45613      */
45614     insertAtCursor : function(text)
45615     {
45616         
45617         if(!this.activated){
45618             return;
45619         }
45620         /*
45621         if(Roo.isIE){
45622             this.win.focus();
45623             var r = this.doc.selection.createRange();
45624             if(r){
45625                 r.collapse(true);
45626                 r.pasteHTML(text);
45627                 this.syncValue();
45628                 this.deferFocus();
45629             
45630             }
45631             return;
45632         }
45633         */
45634         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
45635             this.win.focus();
45636             
45637             
45638             // from jquery ui (MIT licenced)
45639             var range, node;
45640             var win = this.win;
45641             
45642             if (win.getSelection && win.getSelection().getRangeAt) {
45643                 range = win.getSelection().getRangeAt(0);
45644                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
45645                 range.insertNode(node);
45646             } else if (win.document.selection && win.document.selection.createRange) {
45647                 // no firefox support
45648                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45649                 win.document.selection.createRange().pasteHTML(txt);
45650             } else {
45651                 // no firefox support
45652                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45653                 this.execCmd('InsertHTML', txt);
45654             } 
45655             
45656             this.syncValue();
45657             
45658             this.deferFocus();
45659         }
45660     },
45661  // private
45662     mozKeyPress : function(e){
45663         if(e.ctrlKey){
45664             var c = e.getCharCode(), cmd;
45665           
45666             if(c > 0){
45667                 c = String.fromCharCode(c).toLowerCase();
45668                 switch(c){
45669                     case 'b':
45670                         cmd = 'bold';
45671                         break;
45672                     case 'i':
45673                         cmd = 'italic';
45674                         break;
45675                     
45676                     case 'u':
45677                         cmd = 'underline';
45678                         break;
45679                     
45680                     case 'v':
45681                         this.cleanUpPaste.defer(100, this);
45682                         return;
45683                         
45684                 }
45685                 if(cmd){
45686                     this.win.focus();
45687                     this.execCmd(cmd);
45688                     this.deferFocus();
45689                     e.preventDefault();
45690                 }
45691                 
45692             }
45693         }
45694     },
45695
45696     // private
45697     fixKeys : function(){ // load time branching for fastest keydown performance
45698         if(Roo.isIE){
45699             return function(e){
45700                 var k = e.getKey(), r;
45701                 if(k == e.TAB){
45702                     e.stopEvent();
45703                     r = this.doc.selection.createRange();
45704                     if(r){
45705                         r.collapse(true);
45706                         r.pasteHTML('&#160;&#160;&#160;&#160;');
45707                         this.deferFocus();
45708                     }
45709                     return;
45710                 }
45711                 
45712                 if(k == e.ENTER){
45713                     r = this.doc.selection.createRange();
45714                     if(r){
45715                         var target = r.parentElement();
45716                         if(!target || target.tagName.toLowerCase() != 'li'){
45717                             e.stopEvent();
45718                             r.pasteHTML('<br />');
45719                             r.collapse(false);
45720                             r.select();
45721                         }
45722                     }
45723                 }
45724                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45725                     this.cleanUpPaste.defer(100, this);
45726                     return;
45727                 }
45728                 
45729                 
45730             };
45731         }else if(Roo.isOpera){
45732             return function(e){
45733                 var k = e.getKey();
45734                 if(k == e.TAB){
45735                     e.stopEvent();
45736                     this.win.focus();
45737                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
45738                     this.deferFocus();
45739                 }
45740                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45741                     this.cleanUpPaste.defer(100, this);
45742                     return;
45743                 }
45744                 
45745             };
45746         }else if(Roo.isSafari){
45747             return function(e){
45748                 var k = e.getKey();
45749                 
45750                 if(k == e.TAB){
45751                     e.stopEvent();
45752                     this.execCmd('InsertText','\t');
45753                     this.deferFocus();
45754                     return;
45755                 }
45756                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45757                     this.cleanUpPaste.defer(100, this);
45758                     return;
45759                 }
45760                 
45761              };
45762         }
45763     }(),
45764     
45765     getAllAncestors: function()
45766     {
45767         var p = this.getSelectedNode();
45768         var a = [];
45769         if (!p) {
45770             a.push(p); // push blank onto stack..
45771             p = this.getParentElement();
45772         }
45773         
45774         
45775         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
45776             a.push(p);
45777             p = p.parentNode;
45778         }
45779         a.push(this.doc.body);
45780         return a;
45781     },
45782     lastSel : false,
45783     lastSelNode : false,
45784     
45785     
45786     getSelection : function() 
45787     {
45788         this.assignDocWin();
45789         return Roo.isIE ? this.doc.selection : this.win.getSelection();
45790     },
45791     
45792     getSelectedNode: function() 
45793     {
45794         // this may only work on Gecko!!!
45795         
45796         // should we cache this!!!!
45797         
45798         
45799         
45800          
45801         var range = this.createRange(this.getSelection()).cloneRange();
45802         
45803         if (Roo.isIE) {
45804             var parent = range.parentElement();
45805             while (true) {
45806                 var testRange = range.duplicate();
45807                 testRange.moveToElementText(parent);
45808                 if (testRange.inRange(range)) {
45809                     break;
45810                 }
45811                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
45812                     break;
45813                 }
45814                 parent = parent.parentElement;
45815             }
45816             return parent;
45817         }
45818         
45819         // is ancestor a text element.
45820         var ac =  range.commonAncestorContainer;
45821         if (ac.nodeType == 3) {
45822             ac = ac.parentNode;
45823         }
45824         
45825         var ar = ac.childNodes;
45826          
45827         var nodes = [];
45828         var other_nodes = [];
45829         var has_other_nodes = false;
45830         for (var i=0;i<ar.length;i++) {
45831             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
45832                 continue;
45833             }
45834             // fullly contained node.
45835             
45836             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
45837                 nodes.push(ar[i]);
45838                 continue;
45839             }
45840             
45841             // probably selected..
45842             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
45843                 other_nodes.push(ar[i]);
45844                 continue;
45845             }
45846             // outer..
45847             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
45848                 continue;
45849             }
45850             
45851             
45852             has_other_nodes = true;
45853         }
45854         if (!nodes.length && other_nodes.length) {
45855             nodes= other_nodes;
45856         }
45857         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
45858             return false;
45859         }
45860         
45861         return nodes[0];
45862     },
45863     createRange: function(sel)
45864     {
45865         // this has strange effects when using with 
45866         // top toolbar - not sure if it's a great idea.
45867         //this.editor.contentWindow.focus();
45868         if (typeof sel != "undefined") {
45869             try {
45870                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
45871             } catch(e) {
45872                 return this.doc.createRange();
45873             }
45874         } else {
45875             return this.doc.createRange();
45876         }
45877     },
45878     getParentElement: function()
45879     {
45880         
45881         this.assignDocWin();
45882         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
45883         
45884         var range = this.createRange(sel);
45885          
45886         try {
45887             var p = range.commonAncestorContainer;
45888             while (p.nodeType == 3) { // text node
45889                 p = p.parentNode;
45890             }
45891             return p;
45892         } catch (e) {
45893             return null;
45894         }
45895     
45896     },
45897     /***
45898      *
45899      * Range intersection.. the hard stuff...
45900      *  '-1' = before
45901      *  '0' = hits..
45902      *  '1' = after.
45903      *         [ -- selected range --- ]
45904      *   [fail]                        [fail]
45905      *
45906      *    basically..
45907      *      if end is before start or  hits it. fail.
45908      *      if start is after end or hits it fail.
45909      *
45910      *   if either hits (but other is outside. - then it's not 
45911      *   
45912      *    
45913      **/
45914     
45915     
45916     // @see http://www.thismuchiknow.co.uk/?p=64.
45917     rangeIntersectsNode : function(range, node)
45918     {
45919         var nodeRange = node.ownerDocument.createRange();
45920         try {
45921             nodeRange.selectNode(node);
45922         } catch (e) {
45923             nodeRange.selectNodeContents(node);
45924         }
45925     
45926         var rangeStartRange = range.cloneRange();
45927         rangeStartRange.collapse(true);
45928     
45929         var rangeEndRange = range.cloneRange();
45930         rangeEndRange.collapse(false);
45931     
45932         var nodeStartRange = nodeRange.cloneRange();
45933         nodeStartRange.collapse(true);
45934     
45935         var nodeEndRange = nodeRange.cloneRange();
45936         nodeEndRange.collapse(false);
45937     
45938         return rangeStartRange.compareBoundaryPoints(
45939                  Range.START_TO_START, nodeEndRange) == -1 &&
45940                rangeEndRange.compareBoundaryPoints(
45941                  Range.START_TO_START, nodeStartRange) == 1;
45942         
45943          
45944     },
45945     rangeCompareNode : function(range, node)
45946     {
45947         var nodeRange = node.ownerDocument.createRange();
45948         try {
45949             nodeRange.selectNode(node);
45950         } catch (e) {
45951             nodeRange.selectNodeContents(node);
45952         }
45953         
45954         
45955         range.collapse(true);
45956     
45957         nodeRange.collapse(true);
45958      
45959         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
45960         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
45961          
45962         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
45963         
45964         var nodeIsBefore   =  ss == 1;
45965         var nodeIsAfter    = ee == -1;
45966         
45967         if (nodeIsBefore && nodeIsAfter) {
45968             return 0; // outer
45969         }
45970         if (!nodeIsBefore && nodeIsAfter) {
45971             return 1; //right trailed.
45972         }
45973         
45974         if (nodeIsBefore && !nodeIsAfter) {
45975             return 2;  // left trailed.
45976         }
45977         // fully contined.
45978         return 3;
45979     },
45980
45981     // private? - in a new class?
45982     cleanUpPaste :  function()
45983     {
45984         // cleans up the whole document..
45985         Roo.log('cleanuppaste');
45986         
45987         this.cleanUpChild(this.doc.body);
45988         var clean = this.cleanWordChars(this.doc.body.innerHTML);
45989         if (clean != this.doc.body.innerHTML) {
45990             this.doc.body.innerHTML = clean;
45991         }
45992         
45993     },
45994     
45995     cleanWordChars : function(input) {// change the chars to hex code
45996         var he = Roo.HtmlEditorCore;
45997         
45998         var output = input;
45999         Roo.each(he.swapCodes, function(sw) { 
46000             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
46001             
46002             output = output.replace(swapper, sw[1]);
46003         });
46004         
46005         return output;
46006     },
46007     
46008      
46009     
46010         
46011     
46012     cleanUpChild : function (node)
46013     {
46014         
46015         new Roo.htmleditor.FilterComment({node : node});
46016         new Roo.htmleditor.FilterAttributes({
46017                 node : node,
46018                 attrib_black : this.ablack,
46019                 attrib_clean : this.aclean,
46020                 style_white : this.cwhite,
46021                 style_black : this.cblack
46022         });
46023         new Roo.htmleditor.FilterBlack({ node : node, black : this.black});
46024         new Roo.htmleditor.FilterKeepChildren({node : node, black : this.remove} );
46025          
46026         
46027     },
46028     
46029     /**
46030      * Clean up MS wordisms...
46031      * @deprecated - use filter directly
46032      */
46033     cleanWord : function(node)
46034     {
46035         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
46036         
46037     },
46038    
46039     
46040     /**
46041
46042      * @deprecated - use filters
46043      */
46044     cleanTableWidths : function(node)
46045     {
46046         new Roo.htmleditor.FilterTable({ node : node ? node : this.doc.body});
46047         
46048  
46049     },
46050     
46051     
46052     
46053     /* ?? why ?? */
46054     domToHTML : function(currentElement, depth, nopadtext) {
46055         
46056         depth = depth || 0;
46057         nopadtext = nopadtext || false;
46058     
46059         if (!currentElement) {
46060             return this.domToHTML(this.doc.body);
46061         }
46062         
46063         //Roo.log(currentElement);
46064         var j;
46065         var allText = false;
46066         var nodeName = currentElement.nodeName;
46067         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
46068         
46069         if  (nodeName == '#text') {
46070             
46071             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
46072         }
46073         
46074         
46075         var ret = '';
46076         if (nodeName != 'BODY') {
46077              
46078             var i = 0;
46079             // Prints the node tagName, such as <A>, <IMG>, etc
46080             if (tagName) {
46081                 var attr = [];
46082                 for(i = 0; i < currentElement.attributes.length;i++) {
46083                     // quoting?
46084                     var aname = currentElement.attributes.item(i).name;
46085                     if (!currentElement.attributes.item(i).value.length) {
46086                         continue;
46087                     }
46088                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
46089                 }
46090                 
46091                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
46092             } 
46093             else {
46094                 
46095                 // eack
46096             }
46097         } else {
46098             tagName = false;
46099         }
46100         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
46101             return ret;
46102         }
46103         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
46104             nopadtext = true;
46105         }
46106         
46107         
46108         // Traverse the tree
46109         i = 0;
46110         var currentElementChild = currentElement.childNodes.item(i);
46111         var allText = true;
46112         var innerHTML  = '';
46113         lastnode = '';
46114         while (currentElementChild) {
46115             // Formatting code (indent the tree so it looks nice on the screen)
46116             var nopad = nopadtext;
46117             if (lastnode == 'SPAN') {
46118                 nopad  = true;
46119             }
46120             // text
46121             if  (currentElementChild.nodeName == '#text') {
46122                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
46123                 toadd = nopadtext ? toadd : toadd.trim();
46124                 if (!nopad && toadd.length > 80) {
46125                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
46126                 }
46127                 innerHTML  += toadd;
46128                 
46129                 i++;
46130                 currentElementChild = currentElement.childNodes.item(i);
46131                 lastNode = '';
46132                 continue;
46133             }
46134             allText = false;
46135             
46136             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
46137                 
46138             // Recursively traverse the tree structure of the child node
46139             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
46140             lastnode = currentElementChild.nodeName;
46141             i++;
46142             currentElementChild=currentElement.childNodes.item(i);
46143         }
46144         
46145         ret += innerHTML;
46146         
46147         if (!allText) {
46148                 // The remaining code is mostly for formatting the tree
46149             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
46150         }
46151         
46152         
46153         if (tagName) {
46154             ret+= "</"+tagName+">";
46155         }
46156         return ret;
46157         
46158     },
46159         
46160     applyBlacklists : function()
46161     {
46162         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
46163         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
46164         
46165         this.white = [];
46166         this.black = [];
46167         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
46168             if (b.indexOf(tag) > -1) {
46169                 return;
46170             }
46171             this.white.push(tag);
46172             
46173         }, this);
46174         
46175         Roo.each(w, function(tag) {
46176             if (b.indexOf(tag) > -1) {
46177                 return;
46178             }
46179             if (this.white.indexOf(tag) > -1) {
46180                 return;
46181             }
46182             this.white.push(tag);
46183             
46184         }, this);
46185         
46186         
46187         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
46188             if (w.indexOf(tag) > -1) {
46189                 return;
46190             }
46191             this.black.push(tag);
46192             
46193         }, this);
46194         
46195         Roo.each(b, function(tag) {
46196             if (w.indexOf(tag) > -1) {
46197                 return;
46198             }
46199             if (this.black.indexOf(tag) > -1) {
46200                 return;
46201             }
46202             this.black.push(tag);
46203             
46204         }, this);
46205         
46206         
46207         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
46208         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
46209         
46210         this.cwhite = [];
46211         this.cblack = [];
46212         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
46213             if (b.indexOf(tag) > -1) {
46214                 return;
46215             }
46216             this.cwhite.push(tag);
46217             
46218         }, this);
46219         
46220         Roo.each(w, function(tag) {
46221             if (b.indexOf(tag) > -1) {
46222                 return;
46223             }
46224             if (this.cwhite.indexOf(tag) > -1) {
46225                 return;
46226             }
46227             this.cwhite.push(tag);
46228             
46229         }, this);
46230         
46231         
46232         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
46233             if (w.indexOf(tag) > -1) {
46234                 return;
46235             }
46236             this.cblack.push(tag);
46237             
46238         }, this);
46239         
46240         Roo.each(b, function(tag) {
46241             if (w.indexOf(tag) > -1) {
46242                 return;
46243             }
46244             if (this.cblack.indexOf(tag) > -1) {
46245                 return;
46246             }
46247             this.cblack.push(tag);
46248             
46249         }, this);
46250     },
46251     
46252     setStylesheets : function(stylesheets)
46253     {
46254         if(typeof(stylesheets) == 'string'){
46255             Roo.get(this.iframe.contentDocument.head).createChild({
46256                 tag : 'link',
46257                 rel : 'stylesheet',
46258                 type : 'text/css',
46259                 href : stylesheets
46260             });
46261             
46262             return;
46263         }
46264         var _this = this;
46265      
46266         Roo.each(stylesheets, function(s) {
46267             if(!s.length){
46268                 return;
46269             }
46270             
46271             Roo.get(_this.iframe.contentDocument.head).createChild({
46272                 tag : 'link',
46273                 rel : 'stylesheet',
46274                 type : 'text/css',
46275                 href : s
46276             });
46277         });
46278
46279         
46280     },
46281     
46282     removeStylesheets : function()
46283     {
46284         var _this = this;
46285         
46286         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46287             s.remove();
46288         });
46289     },
46290     
46291     setStyle : function(style)
46292     {
46293         Roo.get(this.iframe.contentDocument.head).createChild({
46294             tag : 'style',
46295             type : 'text/css',
46296             html : style
46297         });
46298
46299         return;
46300     }
46301     
46302     // hide stuff that is not compatible
46303     /**
46304      * @event blur
46305      * @hide
46306      */
46307     /**
46308      * @event change
46309      * @hide
46310      */
46311     /**
46312      * @event focus
46313      * @hide
46314      */
46315     /**
46316      * @event specialkey
46317      * @hide
46318      */
46319     /**
46320      * @cfg {String} fieldClass @hide
46321      */
46322     /**
46323      * @cfg {String} focusClass @hide
46324      */
46325     /**
46326      * @cfg {String} autoCreate @hide
46327      */
46328     /**
46329      * @cfg {String} inputType @hide
46330      */
46331     /**
46332      * @cfg {String} invalidClass @hide
46333      */
46334     /**
46335      * @cfg {String} invalidText @hide
46336      */
46337     /**
46338      * @cfg {String} msgFx @hide
46339      */
46340     /**
46341      * @cfg {String} validateOnBlur @hide
46342      */
46343 });
46344
46345 Roo.HtmlEditorCore.white = [
46346         'area', 'br', 'img', 'input', 'hr', 'wbr',
46347         
46348        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
46349        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
46350        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
46351        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
46352        'table',   'ul',         'xmp', 
46353        
46354        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
46355       'thead',   'tr', 
46356      
46357       'dir', 'menu', 'ol', 'ul', 'dl',
46358        
46359       'embed',  'object'
46360 ];
46361
46362
46363 Roo.HtmlEditorCore.black = [
46364     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46365         'applet', // 
46366         'base',   'basefont', 'bgsound', 'blink',  'body', 
46367         'frame',  'frameset', 'head',    'html',   'ilayer', 
46368         'iframe', 'layer',  'link',     'meta',    'object',   
46369         'script', 'style' ,'title',  'xml' // clean later..
46370 ];
46371 Roo.HtmlEditorCore.clean = [
46372     'script', 'style', 'title', 'xml'
46373 ];
46374 Roo.HtmlEditorCore.remove = [
46375     'font'
46376 ];
46377 // attributes..
46378
46379 Roo.HtmlEditorCore.ablack = [
46380     'on'
46381 ];
46382     
46383 Roo.HtmlEditorCore.aclean = [ 
46384     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46385 ];
46386
46387 // protocols..
46388 Roo.HtmlEditorCore.pwhite= [
46389         'http',  'https',  'mailto'
46390 ];
46391
46392 // white listed style attributes.
46393 Roo.HtmlEditorCore.cwhite= [
46394       //  'text-align', /// default is to allow most things..
46395       
46396          
46397 //        'font-size'//??
46398 ];
46399
46400 // black listed style attributes.
46401 Roo.HtmlEditorCore.cblack= [
46402       //  'font-size' -- this can be set by the project 
46403 ];
46404
46405
46406 Roo.HtmlEditorCore.swapCodes   =[ 
46407     [    8211, "&#8211;" ], 
46408     [    8212, "&#8212;" ], 
46409     [    8216,  "'" ],  
46410     [    8217, "'" ],  
46411     [    8220, '"' ],  
46412     [    8221, '"' ],  
46413     [    8226, "*" ],  
46414     [    8230, "..." ]
46415 ]; 
46416
46417     //<script type="text/javascript">
46418
46419 /*
46420  * Ext JS Library 1.1.1
46421  * Copyright(c) 2006-2007, Ext JS, LLC.
46422  * Licence LGPL
46423  * 
46424  */
46425  
46426  
46427 Roo.form.HtmlEditor = function(config){
46428     
46429     
46430     
46431     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
46432     
46433     if (!this.toolbars) {
46434         this.toolbars = [];
46435     }
46436     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
46437     
46438     
46439 };
46440
46441 /**
46442  * @class Roo.form.HtmlEditor
46443  * @extends Roo.form.Field
46444  * Provides a lightweight HTML Editor component.
46445  *
46446  * This has been tested on Fireforx / Chrome.. IE may not be so great..
46447  * 
46448  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
46449  * supported by this editor.</b><br/><br/>
46450  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
46451  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46452  */
46453 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
46454     /**
46455      * @cfg {Boolean} clearUp
46456      */
46457     clearUp : true,
46458       /**
46459      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
46460      */
46461     toolbars : false,
46462    
46463      /**
46464      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46465      *                        Roo.resizable.
46466      */
46467     resizable : false,
46468      /**
46469      * @cfg {Number} height (in pixels)
46470      */   
46471     height: 300,
46472    /**
46473      * @cfg {Number} width (in pixels)
46474      */   
46475     width: 500,
46476     
46477     /**
46478      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
46479      * 
46480      */
46481     stylesheets: false,
46482     
46483     
46484      /**
46485      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
46486      * 
46487      */
46488     cblack: false,
46489     /**
46490      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
46491      * 
46492      */
46493     cwhite: false,
46494     
46495      /**
46496      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
46497      * 
46498      */
46499     black: false,
46500     /**
46501      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
46502      * 
46503      */
46504     white: false,
46505     /**
46506      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46507      */
46508     allowComments: false,
46509     /**
46510      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
46511      */
46512     
46513     
46514      bodyCls : '',
46515     
46516     // id of frame..
46517     frameId: false,
46518     
46519     // private properties
46520     validationEvent : false,
46521     deferHeight: true,
46522     initialized : false,
46523     activated : false,
46524     
46525     onFocus : Roo.emptyFn,
46526     iframePad:3,
46527     hideMode:'offsets',
46528     
46529     actionMode : 'container', // defaults to hiding it...
46530     
46531     defaultAutoCreate : { // modified by initCompnoent..
46532         tag: "textarea",
46533         style:"width:500px;height:300px;",
46534         autocomplete: "new-password"
46535     },
46536
46537     // private
46538     initComponent : function(){
46539         this.addEvents({
46540             /**
46541              * @event initialize
46542              * Fires when the editor is fully initialized (including the iframe)
46543              * @param {HtmlEditor} this
46544              */
46545             initialize: true,
46546             /**
46547              * @event activate
46548              * Fires when the editor is first receives the focus. Any insertion must wait
46549              * until after this event.
46550              * @param {HtmlEditor} this
46551              */
46552             activate: true,
46553              /**
46554              * @event beforesync
46555              * Fires before the textarea is updated with content from the editor iframe. Return false
46556              * to cancel the sync.
46557              * @param {HtmlEditor} this
46558              * @param {String} html
46559              */
46560             beforesync: true,
46561              /**
46562              * @event beforepush
46563              * Fires before the iframe editor is updated with content from the textarea. Return false
46564              * to cancel the push.
46565              * @param {HtmlEditor} this
46566              * @param {String} html
46567              */
46568             beforepush: true,
46569              /**
46570              * @event sync
46571              * Fires when the textarea is updated with content from the editor iframe.
46572              * @param {HtmlEditor} this
46573              * @param {String} html
46574              */
46575             sync: true,
46576              /**
46577              * @event push
46578              * Fires when the iframe editor is updated with content from the textarea.
46579              * @param {HtmlEditor} this
46580              * @param {String} html
46581              */
46582             push: true,
46583              /**
46584              * @event editmodechange
46585              * Fires when the editor switches edit modes
46586              * @param {HtmlEditor} this
46587              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
46588              */
46589             editmodechange: true,
46590             /**
46591              * @event editorevent
46592              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46593              * @param {HtmlEditor} this
46594              */
46595             editorevent: true,
46596             /**
46597              * @event firstfocus
46598              * Fires when on first focus - needed by toolbars..
46599              * @param {HtmlEditor} this
46600              */
46601             firstfocus: true,
46602             /**
46603              * @event autosave
46604              * Auto save the htmlEditor value as a file into Events
46605              * @param {HtmlEditor} this
46606              */
46607             autosave: true,
46608             /**
46609              * @event savedpreview
46610              * preview the saved version of htmlEditor
46611              * @param {HtmlEditor} this
46612              */
46613             savedpreview: true,
46614             
46615             /**
46616             * @event stylesheetsclick
46617             * Fires when press the Sytlesheets button
46618             * @param {Roo.HtmlEditorCore} this
46619             */
46620             stylesheetsclick: true
46621         });
46622         this.defaultAutoCreate =  {
46623             tag: "textarea",
46624             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
46625             autocomplete: "new-password"
46626         };
46627     },
46628
46629     /**
46630      * Protected method that will not generally be called directly. It
46631      * is called when the editor creates its toolbar. Override this method if you need to
46632      * add custom toolbar buttons.
46633      * @param {HtmlEditor} editor
46634      */
46635     createToolbar : function(editor){
46636         Roo.log("create toolbars");
46637         if (!editor.toolbars || !editor.toolbars.length) {
46638             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
46639         }
46640         
46641         for (var i =0 ; i < editor.toolbars.length;i++) {
46642             editor.toolbars[i] = Roo.factory(
46643                     typeof(editor.toolbars[i]) == 'string' ?
46644                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
46645                 Roo.form.HtmlEditor);
46646             editor.toolbars[i].init(editor);
46647         }
46648          
46649         
46650     },
46651
46652      
46653     // private
46654     onRender : function(ct, position)
46655     {
46656         var _t = this;
46657         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
46658         
46659         this.wrap = this.el.wrap({
46660             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
46661         });
46662         
46663         this.editorcore.onRender(ct, position);
46664          
46665         if (this.resizable) {
46666             this.resizeEl = new Roo.Resizable(this.wrap, {
46667                 pinned : true,
46668                 wrap: true,
46669                 dynamic : true,
46670                 minHeight : this.height,
46671                 height: this.height,
46672                 handles : this.resizable,
46673                 width: this.width,
46674                 listeners : {
46675                     resize : function(r, w, h) {
46676                         _t.onResize(w,h); // -something
46677                     }
46678                 }
46679             });
46680             
46681         }
46682         this.createToolbar(this);
46683        
46684         
46685         if(!this.width){
46686             this.setSize(this.wrap.getSize());
46687         }
46688         if (this.resizeEl) {
46689             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
46690             // should trigger onReize..
46691         }
46692         
46693         this.keyNav = new Roo.KeyNav(this.el, {
46694             
46695             "tab" : function(e){
46696                 e.preventDefault();
46697                 
46698                 var value = this.getValue();
46699                 
46700                 var start = this.el.dom.selectionStart;
46701                 var end = this.el.dom.selectionEnd;
46702                 
46703                 if(!e.shiftKey){
46704                     
46705                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
46706                     this.el.dom.setSelectionRange(end + 1, end + 1);
46707                     return;
46708                 }
46709                 
46710                 var f = value.substring(0, start).split("\t");
46711                 
46712                 if(f.pop().length != 0){
46713                     return;
46714                 }
46715                 
46716                 this.setValue(f.join("\t") + value.substring(end));
46717                 this.el.dom.setSelectionRange(start - 1, start - 1);
46718                 
46719             },
46720             
46721             "home" : function(e){
46722                 e.preventDefault();
46723                 
46724                 var curr = this.el.dom.selectionStart;
46725                 var lines = this.getValue().split("\n");
46726                 
46727                 if(!lines.length){
46728                     return;
46729                 }
46730                 
46731                 if(e.ctrlKey){
46732                     this.el.dom.setSelectionRange(0, 0);
46733                     return;
46734                 }
46735                 
46736                 var pos = 0;
46737                 
46738                 for (var i = 0; i < lines.length;i++) {
46739                     pos += lines[i].length;
46740                     
46741                     if(i != 0){
46742                         pos += 1;
46743                     }
46744                     
46745                     if(pos < curr){
46746                         continue;
46747                     }
46748                     
46749                     pos -= lines[i].length;
46750                     
46751                     break;
46752                 }
46753                 
46754                 if(!e.shiftKey){
46755                     this.el.dom.setSelectionRange(pos, pos);
46756                     return;
46757                 }
46758                 
46759                 this.el.dom.selectionStart = pos;
46760                 this.el.dom.selectionEnd = curr;
46761             },
46762             
46763             "end" : function(e){
46764                 e.preventDefault();
46765                 
46766                 var curr = this.el.dom.selectionStart;
46767                 var lines = this.getValue().split("\n");
46768                 
46769                 if(!lines.length){
46770                     return;
46771                 }
46772                 
46773                 if(e.ctrlKey){
46774                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
46775                     return;
46776                 }
46777                 
46778                 var pos = 0;
46779                 
46780                 for (var i = 0; i < lines.length;i++) {
46781                     
46782                     pos += lines[i].length;
46783                     
46784                     if(i != 0){
46785                         pos += 1;
46786                     }
46787                     
46788                     if(pos < curr){
46789                         continue;
46790                     }
46791                     
46792                     break;
46793                 }
46794                 
46795                 if(!e.shiftKey){
46796                     this.el.dom.setSelectionRange(pos, pos);
46797                     return;
46798                 }
46799                 
46800                 this.el.dom.selectionStart = curr;
46801                 this.el.dom.selectionEnd = pos;
46802             },
46803
46804             scope : this,
46805
46806             doRelay : function(foo, bar, hname){
46807                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46808             },
46809
46810             forceKeyDown: true
46811         });
46812         
46813 //        if(this.autosave && this.w){
46814 //            this.autoSaveFn = setInterval(this.autosave, 1000);
46815 //        }
46816     },
46817
46818     // private
46819     onResize : function(w, h)
46820     {
46821         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
46822         var ew = false;
46823         var eh = false;
46824         
46825         if(this.el ){
46826             if(typeof w == 'number'){
46827                 var aw = w - this.wrap.getFrameWidth('lr');
46828                 this.el.setWidth(this.adjustWidth('textarea', aw));
46829                 ew = aw;
46830             }
46831             if(typeof h == 'number'){
46832                 var tbh = 0;
46833                 for (var i =0; i < this.toolbars.length;i++) {
46834                     // fixme - ask toolbars for heights?
46835                     tbh += this.toolbars[i].tb.el.getHeight();
46836                     if (this.toolbars[i].footer) {
46837                         tbh += this.toolbars[i].footer.el.getHeight();
46838                     }
46839                 }
46840                 
46841                 
46842                 
46843                 
46844                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
46845                 ah -= 5; // knock a few pixes off for look..
46846 //                Roo.log(ah);
46847                 this.el.setHeight(this.adjustWidth('textarea', ah));
46848                 var eh = ah;
46849             }
46850         }
46851         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
46852         this.editorcore.onResize(ew,eh);
46853         
46854     },
46855
46856     /**
46857      * Toggles the editor between standard and source edit mode.
46858      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46859      */
46860     toggleSourceEdit : function(sourceEditMode)
46861     {
46862         this.editorcore.toggleSourceEdit(sourceEditMode);
46863         
46864         if(this.editorcore.sourceEditMode){
46865             Roo.log('editor - showing textarea');
46866             
46867 //            Roo.log('in');
46868 //            Roo.log(this.syncValue());
46869             this.editorcore.syncValue();
46870             this.el.removeClass('x-hidden');
46871             this.el.dom.removeAttribute('tabIndex');
46872             this.el.focus();
46873             
46874             for (var i = 0; i < this.toolbars.length; i++) {
46875                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46876                     this.toolbars[i].tb.hide();
46877                     this.toolbars[i].footer.hide();
46878                 }
46879             }
46880             
46881         }else{
46882             Roo.log('editor - hiding textarea');
46883 //            Roo.log('out')
46884 //            Roo.log(this.pushValue()); 
46885             this.editorcore.pushValue();
46886             
46887             this.el.addClass('x-hidden');
46888             this.el.dom.setAttribute('tabIndex', -1);
46889             
46890             for (var i = 0; i < this.toolbars.length; i++) {
46891                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46892                     this.toolbars[i].tb.show();
46893                     this.toolbars[i].footer.show();
46894                 }
46895             }
46896             
46897             //this.deferFocus();
46898         }
46899         
46900         this.setSize(this.wrap.getSize());
46901         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
46902         
46903         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
46904     },
46905  
46906     // private (for BoxComponent)
46907     adjustSize : Roo.BoxComponent.prototype.adjustSize,
46908
46909     // private (for BoxComponent)
46910     getResizeEl : function(){
46911         return this.wrap;
46912     },
46913
46914     // private (for BoxComponent)
46915     getPositionEl : function(){
46916         return this.wrap;
46917     },
46918
46919     // private
46920     initEvents : function(){
46921         this.originalValue = this.getValue();
46922     },
46923
46924     /**
46925      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46926      * @method
46927      */
46928     markInvalid : Roo.emptyFn,
46929     /**
46930      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46931      * @method
46932      */
46933     clearInvalid : Roo.emptyFn,
46934
46935     setValue : function(v){
46936         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
46937         this.editorcore.pushValue();
46938     },
46939
46940      
46941     // private
46942     deferFocus : function(){
46943         this.focus.defer(10, this);
46944     },
46945
46946     // doc'ed in Field
46947     focus : function(){
46948         this.editorcore.focus();
46949         
46950     },
46951       
46952
46953     // private
46954     onDestroy : function(){
46955         
46956         
46957         
46958         if(this.rendered){
46959             
46960             for (var i =0; i < this.toolbars.length;i++) {
46961                 // fixme - ask toolbars for heights?
46962                 this.toolbars[i].onDestroy();
46963             }
46964             
46965             this.wrap.dom.innerHTML = '';
46966             this.wrap.remove();
46967         }
46968     },
46969
46970     // private
46971     onFirstFocus : function(){
46972         //Roo.log("onFirstFocus");
46973         this.editorcore.onFirstFocus();
46974          for (var i =0; i < this.toolbars.length;i++) {
46975             this.toolbars[i].onFirstFocus();
46976         }
46977         
46978     },
46979     
46980     // private
46981     syncValue : function()
46982     {
46983         this.editorcore.syncValue();
46984     },
46985     
46986     pushValue : function()
46987     {
46988         this.editorcore.pushValue();
46989     },
46990     
46991     setStylesheets : function(stylesheets)
46992     {
46993         this.editorcore.setStylesheets(stylesheets);
46994     },
46995     
46996     removeStylesheets : function()
46997     {
46998         this.editorcore.removeStylesheets();
46999     }
47000      
47001     
47002     // hide stuff that is not compatible
47003     /**
47004      * @event blur
47005      * @hide
47006      */
47007     /**
47008      * @event change
47009      * @hide
47010      */
47011     /**
47012      * @event focus
47013      * @hide
47014      */
47015     /**
47016      * @event specialkey
47017      * @hide
47018      */
47019     /**
47020      * @cfg {String} fieldClass @hide
47021      */
47022     /**
47023      * @cfg {String} focusClass @hide
47024      */
47025     /**
47026      * @cfg {String} autoCreate @hide
47027      */
47028     /**
47029      * @cfg {String} inputType @hide
47030      */
47031     /**
47032      * @cfg {String} invalidClass @hide
47033      */
47034     /**
47035      * @cfg {String} invalidText @hide
47036      */
47037     /**
47038      * @cfg {String} msgFx @hide
47039      */
47040     /**
47041      * @cfg {String} validateOnBlur @hide
47042      */
47043 });
47044  
47045     // <script type="text/javascript">
47046 /*
47047  * Based on
47048  * Ext JS Library 1.1.1
47049  * Copyright(c) 2006-2007, Ext JS, LLC.
47050  *  
47051  
47052  */
47053
47054 /**
47055  * @class Roo.form.HtmlEditorToolbar1
47056  * Basic Toolbar
47057  * 
47058  * Usage:
47059  *
47060  new Roo.form.HtmlEditor({
47061     ....
47062     toolbars : [
47063         new Roo.form.HtmlEditorToolbar1({
47064             disable : { fonts: 1 , format: 1, ..., ... , ...],
47065             btns : [ .... ]
47066         })
47067     }
47068      
47069  * 
47070  * @cfg {Object} disable List of elements to disable..
47071  * @cfg {Array} btns List of additional buttons.
47072  * 
47073  * 
47074  * NEEDS Extra CSS? 
47075  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
47076  */
47077  
47078 Roo.form.HtmlEditor.ToolbarStandard = function(config)
47079 {
47080     
47081     Roo.apply(this, config);
47082     
47083     // default disabled, based on 'good practice'..
47084     this.disable = this.disable || {};
47085     Roo.applyIf(this.disable, {
47086         fontSize : true,
47087         colors : true,
47088         specialElements : true
47089     });
47090     
47091     
47092     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47093     // dont call parent... till later.
47094 }
47095
47096 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
47097     
47098     tb: false,
47099     
47100     rendered: false,
47101     
47102     editor : false,
47103     editorcore : false,
47104     /**
47105      * @cfg {Object} disable  List of toolbar elements to disable
47106          
47107      */
47108     disable : false,
47109     
47110     
47111      /**
47112      * @cfg {String} createLinkText The default text for the create link prompt
47113      */
47114     createLinkText : 'Please enter the URL for the link:',
47115     /**
47116      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
47117      */
47118     defaultLinkValue : 'http:/'+'/',
47119    
47120     
47121       /**
47122      * @cfg {Array} fontFamilies An array of available font families
47123      */
47124     fontFamilies : [
47125         'Arial',
47126         'Courier New',
47127         'Tahoma',
47128         'Times New Roman',
47129         'Verdana'
47130     ],
47131     
47132     specialChars : [
47133            "&#169;",
47134           "&#174;",     
47135           "&#8482;",    
47136           "&#163;" ,    
47137          // "&#8212;",    
47138           "&#8230;",    
47139           "&#247;" ,    
47140         //  "&#225;" ,     ?? a acute?
47141            "&#8364;"    , //Euro
47142        //   "&#8220;"    ,
47143         //  "&#8221;"    ,
47144         //  "&#8226;"    ,
47145           "&#176;"  //   , // degrees
47146
47147          // "&#233;"     , // e ecute
47148          // "&#250;"     , // u ecute?
47149     ],
47150     
47151     specialElements : [
47152         {
47153             text: "Insert Table",
47154             xtype: 'MenuItem',
47155             xns : Roo.Menu,
47156             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
47157                 
47158         },
47159         {    
47160             text: "Insert Image",
47161             xtype: 'MenuItem',
47162             xns : Roo.Menu,
47163             ihtml : '<img src="about:blank"/>'
47164             
47165         }
47166         
47167          
47168     ],
47169     
47170     
47171     inputElements : [ 
47172             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
47173             "input:submit", "input:button", "select", "textarea", "label" ],
47174     formats : [
47175         ["p"] ,  
47176         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
47177         ["pre"],[ "code"], 
47178         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
47179         ['div'],['span'],
47180         ['sup'],['sub']
47181     ],
47182     
47183     cleanStyles : [
47184         "font-size"
47185     ],
47186      /**
47187      * @cfg {String} defaultFont default font to use.
47188      */
47189     defaultFont: 'tahoma',
47190    
47191     fontSelect : false,
47192     
47193     
47194     formatCombo : false,
47195     
47196     init : function(editor)
47197     {
47198         this.editor = editor;
47199         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47200         var editorcore = this.editorcore;
47201         
47202         var _t = this;
47203         
47204         var fid = editorcore.frameId;
47205         var etb = this;
47206         function btn(id, toggle, handler){
47207             var xid = fid + '-'+ id ;
47208             return {
47209                 id : xid,
47210                 cmd : id,
47211                 cls : 'x-btn-icon x-edit-'+id,
47212                 enableToggle:toggle !== false,
47213                 scope: _t, // was editor...
47214                 handler:handler||_t.relayBtnCmd,
47215                 clickEvent:'mousedown',
47216                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47217                 tabIndex:-1
47218             };
47219         }
47220         
47221         
47222         
47223         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47224         this.tb = tb;
47225          // stop form submits
47226         tb.el.on('click', function(e){
47227             e.preventDefault(); // what does this do?
47228         });
47229
47230         if(!this.disable.font) { // && !Roo.isSafari){
47231             /* why no safari for fonts 
47232             editor.fontSelect = tb.el.createChild({
47233                 tag:'select',
47234                 tabIndex: -1,
47235                 cls:'x-font-select',
47236                 html: this.createFontOptions()
47237             });
47238             
47239             editor.fontSelect.on('change', function(){
47240                 var font = editor.fontSelect.dom.value;
47241                 editor.relayCmd('fontname', font);
47242                 editor.deferFocus();
47243             }, editor);
47244             
47245             tb.add(
47246                 editor.fontSelect.dom,
47247                 '-'
47248             );
47249             */
47250             
47251         };
47252         if(!this.disable.formats){
47253             this.formatCombo = new Roo.form.ComboBox({
47254                 store: new Roo.data.SimpleStore({
47255                     id : 'tag',
47256                     fields: ['tag'],
47257                     data : this.formats // from states.js
47258                 }),
47259                 blockFocus : true,
47260                 name : '',
47261                 //autoCreate : {tag: "div",  size: "20"},
47262                 displayField:'tag',
47263                 typeAhead: false,
47264                 mode: 'local',
47265                 editable : false,
47266                 triggerAction: 'all',
47267                 emptyText:'Add tag',
47268                 selectOnFocus:true,
47269                 width:135,
47270                 listeners : {
47271                     'select': function(c, r, i) {
47272                         editorcore.insertTag(r.get('tag'));
47273                         editor.focus();
47274                     }
47275                 }
47276
47277             });
47278             tb.addField(this.formatCombo);
47279             
47280         }
47281         
47282         if(!this.disable.format){
47283             tb.add(
47284                 btn('bold'),
47285                 btn('italic'),
47286                 btn('underline'),
47287                 btn('strikethrough')
47288             );
47289         };
47290         if(!this.disable.fontSize){
47291             tb.add(
47292                 '-',
47293                 
47294                 
47295                 btn('increasefontsize', false, editorcore.adjustFont),
47296                 btn('decreasefontsize', false, editorcore.adjustFont)
47297             );
47298         };
47299         
47300         
47301         if(!this.disable.colors){
47302             tb.add(
47303                 '-', {
47304                     id:editorcore.frameId +'-forecolor',
47305                     cls:'x-btn-icon x-edit-forecolor',
47306                     clickEvent:'mousedown',
47307                     tooltip: this.buttonTips['forecolor'] || undefined,
47308                     tabIndex:-1,
47309                     menu : new Roo.menu.ColorMenu({
47310                         allowReselect: true,
47311                         focus: Roo.emptyFn,
47312                         value:'000000',
47313                         plain:true,
47314                         selectHandler: function(cp, color){
47315                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47316                             editor.deferFocus();
47317                         },
47318                         scope: editorcore,
47319                         clickEvent:'mousedown'
47320                     })
47321                 }, {
47322                     id:editorcore.frameId +'backcolor',
47323                     cls:'x-btn-icon x-edit-backcolor',
47324                     clickEvent:'mousedown',
47325                     tooltip: this.buttonTips['backcolor'] || undefined,
47326                     tabIndex:-1,
47327                     menu : new Roo.menu.ColorMenu({
47328                         focus: Roo.emptyFn,
47329                         value:'FFFFFF',
47330                         plain:true,
47331                         allowReselect: true,
47332                         selectHandler: function(cp, color){
47333                             if(Roo.isGecko){
47334                                 editorcore.execCmd('useCSS', false);
47335                                 editorcore.execCmd('hilitecolor', color);
47336                                 editorcore.execCmd('useCSS', true);
47337                                 editor.deferFocus();
47338                             }else{
47339                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47340                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47341                                 editor.deferFocus();
47342                             }
47343                         },
47344                         scope:editorcore,
47345                         clickEvent:'mousedown'
47346                     })
47347                 }
47348             );
47349         };
47350         // now add all the items...
47351         
47352
47353         if(!this.disable.alignments){
47354             tb.add(
47355                 '-',
47356                 btn('justifyleft'),
47357                 btn('justifycenter'),
47358                 btn('justifyright')
47359             );
47360         };
47361
47362         //if(!Roo.isSafari){
47363             if(!this.disable.links){
47364                 tb.add(
47365                     '-',
47366                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47367                 );
47368             };
47369
47370             if(!this.disable.lists){
47371                 tb.add(
47372                     '-',
47373                     btn('insertorderedlist'),
47374                     btn('insertunorderedlist')
47375                 );
47376             }
47377             if(!this.disable.sourceEdit){
47378                 tb.add(
47379                     '-',
47380                     btn('sourceedit', true, function(btn){
47381                         this.toggleSourceEdit(btn.pressed);
47382                     })
47383                 );
47384             }
47385         //}
47386         
47387         var smenu = { };
47388         // special menu.. - needs to be tidied up..
47389         if (!this.disable.special) {
47390             smenu = {
47391                 text: "&#169;",
47392                 cls: 'x-edit-none',
47393                 
47394                 menu : {
47395                     items : []
47396                 }
47397             };
47398             for (var i =0; i < this.specialChars.length; i++) {
47399                 smenu.menu.items.push({
47400                     
47401                     html: this.specialChars[i],
47402                     handler: function(a,b) {
47403                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47404                         //editor.insertAtCursor(a.html);
47405                         
47406                     },
47407                     tabIndex:-1
47408                 });
47409             }
47410             
47411             
47412             tb.add(smenu);
47413             
47414             
47415         }
47416         
47417         var cmenu = { };
47418         if (!this.disable.cleanStyles) {
47419             cmenu = {
47420                 cls: 'x-btn-icon x-btn-clear',
47421                 
47422                 menu : {
47423                     items : []
47424                 }
47425             };
47426             for (var i =0; i < this.cleanStyles.length; i++) {
47427                 cmenu.menu.items.push({
47428                     actiontype : this.cleanStyles[i],
47429                     html: 'Remove ' + this.cleanStyles[i],
47430                     handler: function(a,b) {
47431 //                        Roo.log(a);
47432 //                        Roo.log(b);
47433                         var c = Roo.get(editorcore.doc.body);
47434                         c.select('[style]').each(function(s) {
47435                             s.dom.style.removeProperty(a.actiontype);
47436                         });
47437                         editorcore.syncValue();
47438                     },
47439                     tabIndex:-1
47440                 });
47441             }
47442              cmenu.menu.items.push({
47443                 actiontype : 'tablewidths',
47444                 html: 'Remove Table Widths',
47445                 handler: function(a,b) {
47446                     editorcore.cleanTableWidths();
47447                     editorcore.syncValue();
47448                 },
47449                 tabIndex:-1
47450             });
47451             cmenu.menu.items.push({
47452                 actiontype : 'word',
47453                 html: 'Remove MS Word Formating',
47454                 handler: function(a,b) {
47455                     editorcore.cleanWord();
47456                     editorcore.syncValue();
47457                 },
47458                 tabIndex:-1
47459             });
47460             
47461             cmenu.menu.items.push({
47462                 actiontype : 'all',
47463                 html: 'Remove All Styles',
47464                 handler: function(a,b) {
47465                     
47466                     var c = Roo.get(editorcore.doc.body);
47467                     c.select('[style]').each(function(s) {
47468                         s.dom.removeAttribute('style');
47469                     });
47470                     editorcore.syncValue();
47471                 },
47472                 tabIndex:-1
47473             });
47474             
47475             cmenu.menu.items.push({
47476                 actiontype : 'all',
47477                 html: 'Remove All CSS Classes',
47478                 handler: function(a,b) {
47479                     
47480                     var c = Roo.get(editorcore.doc.body);
47481                     c.select('[class]').each(function(s) {
47482                         s.dom.removeAttribute('class');
47483                     });
47484                     editorcore.cleanWord();
47485                     editorcore.syncValue();
47486                 },
47487                 tabIndex:-1
47488             });
47489             
47490              cmenu.menu.items.push({
47491                 actiontype : 'tidy',
47492                 html: 'Tidy HTML Source',
47493                 handler: function(a,b) {
47494                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
47495                     editorcore.syncValue();
47496                 },
47497                 tabIndex:-1
47498             });
47499             
47500             
47501             tb.add(cmenu);
47502         }
47503          
47504         if (!this.disable.specialElements) {
47505             var semenu = {
47506                 text: "Other;",
47507                 cls: 'x-edit-none',
47508                 menu : {
47509                     items : []
47510                 }
47511             };
47512             for (var i =0; i < this.specialElements.length; i++) {
47513                 semenu.menu.items.push(
47514                     Roo.apply({ 
47515                         handler: function(a,b) {
47516                             editor.insertAtCursor(this.ihtml);
47517                         }
47518                     }, this.specialElements[i])
47519                 );
47520                     
47521             }
47522             
47523             tb.add(semenu);
47524             
47525             
47526         }
47527          
47528         
47529         if (this.btns) {
47530             for(var i =0; i< this.btns.length;i++) {
47531                 var b = Roo.factory(this.btns[i],Roo.form);
47532                 b.cls =  'x-edit-none';
47533                 
47534                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
47535                     b.cls += ' x-init-enable';
47536                 }
47537                 
47538                 b.scope = editorcore;
47539                 tb.add(b);
47540             }
47541         
47542         }
47543         
47544         
47545         
47546         // disable everything...
47547         
47548         this.tb.items.each(function(item){
47549             
47550            if(
47551                 item.id != editorcore.frameId+ '-sourceedit' && 
47552                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
47553             ){
47554                 
47555                 item.disable();
47556             }
47557         });
47558         this.rendered = true;
47559         
47560         // the all the btns;
47561         editor.on('editorevent', this.updateToolbar, this);
47562         // other toolbars need to implement this..
47563         //editor.on('editmodechange', this.updateToolbar, this);
47564     },
47565     
47566     
47567     relayBtnCmd : function(btn) {
47568         this.editorcore.relayCmd(btn.cmd);
47569     },
47570     // private used internally
47571     createLink : function(){
47572         Roo.log("create link?");
47573         var url = prompt(this.createLinkText, this.defaultLinkValue);
47574         if(url && url != 'http:/'+'/'){
47575             this.editorcore.relayCmd('createlink', url);
47576         }
47577     },
47578
47579     
47580     /**
47581      * Protected method that will not generally be called directly. It triggers
47582      * a toolbar update by reading the markup state of the current selection in the editor.
47583      */
47584     updateToolbar: function(){
47585
47586         if(!this.editorcore.activated){
47587             this.editor.onFirstFocus();
47588             return;
47589         }
47590
47591         var btns = this.tb.items.map, 
47592             doc = this.editorcore.doc,
47593             frameId = this.editorcore.frameId;
47594
47595         if(!this.disable.font && !Roo.isSafari){
47596             /*
47597             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
47598             if(name != this.fontSelect.dom.value){
47599                 this.fontSelect.dom.value = name;
47600             }
47601             */
47602         }
47603         if(!this.disable.format){
47604             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
47605             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
47606             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
47607             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
47608         }
47609         if(!this.disable.alignments){
47610             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
47611             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
47612             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
47613         }
47614         if(!Roo.isSafari && !this.disable.lists){
47615             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
47616             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
47617         }
47618         
47619         var ans = this.editorcore.getAllAncestors();
47620         if (this.formatCombo) {
47621             
47622             
47623             var store = this.formatCombo.store;
47624             this.formatCombo.setValue("");
47625             for (var i =0; i < ans.length;i++) {
47626                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
47627                     // select it..
47628                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
47629                     break;
47630                 }
47631             }
47632         }
47633         
47634         
47635         
47636         // hides menus... - so this cant be on a menu...
47637         Roo.menu.MenuMgr.hideAll();
47638
47639         //this.editorsyncValue();
47640     },
47641    
47642     
47643     createFontOptions : function(){
47644         var buf = [], fs = this.fontFamilies, ff, lc;
47645         
47646         
47647         
47648         for(var i = 0, len = fs.length; i< len; i++){
47649             ff = fs[i];
47650             lc = ff.toLowerCase();
47651             buf.push(
47652                 '<option value="',lc,'" style="font-family:',ff,';"',
47653                     (this.defaultFont == lc ? ' selected="true">' : '>'),
47654                     ff,
47655                 '</option>'
47656             );
47657         }
47658         return buf.join('');
47659     },
47660     
47661     toggleSourceEdit : function(sourceEditMode){
47662         
47663         Roo.log("toolbar toogle");
47664         if(sourceEditMode === undefined){
47665             sourceEditMode = !this.sourceEditMode;
47666         }
47667         this.sourceEditMode = sourceEditMode === true;
47668         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
47669         // just toggle the button?
47670         if(btn.pressed !== this.sourceEditMode){
47671             btn.toggle(this.sourceEditMode);
47672             return;
47673         }
47674         
47675         if(sourceEditMode){
47676             Roo.log("disabling buttons");
47677             this.tb.items.each(function(item){
47678                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
47679                     item.disable();
47680                 }
47681             });
47682           
47683         }else{
47684             Roo.log("enabling buttons");
47685             if(this.editorcore.initialized){
47686                 this.tb.items.each(function(item){
47687                     item.enable();
47688                 });
47689             }
47690             
47691         }
47692         Roo.log("calling toggole on editor");
47693         // tell the editor that it's been pressed..
47694         this.editor.toggleSourceEdit(sourceEditMode);
47695        
47696     },
47697      /**
47698      * Object collection of toolbar tooltips for the buttons in the editor. The key
47699      * is the command id associated with that button and the value is a valid QuickTips object.
47700      * For example:
47701 <pre><code>
47702 {
47703     bold : {
47704         title: 'Bold (Ctrl+B)',
47705         text: 'Make the selected text bold.',
47706         cls: 'x-html-editor-tip'
47707     },
47708     italic : {
47709         title: 'Italic (Ctrl+I)',
47710         text: 'Make the selected text italic.',
47711         cls: 'x-html-editor-tip'
47712     },
47713     ...
47714 </code></pre>
47715     * @type Object
47716      */
47717     buttonTips : {
47718         bold : {
47719             title: 'Bold (Ctrl+B)',
47720             text: 'Make the selected text bold.',
47721             cls: 'x-html-editor-tip'
47722         },
47723         italic : {
47724             title: 'Italic (Ctrl+I)',
47725             text: 'Make the selected text italic.',
47726             cls: 'x-html-editor-tip'
47727         },
47728         underline : {
47729             title: 'Underline (Ctrl+U)',
47730             text: 'Underline the selected text.',
47731             cls: 'x-html-editor-tip'
47732         },
47733         strikethrough : {
47734             title: 'Strikethrough',
47735             text: 'Strikethrough the selected text.',
47736             cls: 'x-html-editor-tip'
47737         },
47738         increasefontsize : {
47739             title: 'Grow Text',
47740             text: 'Increase the font size.',
47741             cls: 'x-html-editor-tip'
47742         },
47743         decreasefontsize : {
47744             title: 'Shrink Text',
47745             text: 'Decrease the font size.',
47746             cls: 'x-html-editor-tip'
47747         },
47748         backcolor : {
47749             title: 'Text Highlight Color',
47750             text: 'Change the background color of the selected text.',
47751             cls: 'x-html-editor-tip'
47752         },
47753         forecolor : {
47754             title: 'Font Color',
47755             text: 'Change the color of the selected text.',
47756             cls: 'x-html-editor-tip'
47757         },
47758         justifyleft : {
47759             title: 'Align Text Left',
47760             text: 'Align text to the left.',
47761             cls: 'x-html-editor-tip'
47762         },
47763         justifycenter : {
47764             title: 'Center Text',
47765             text: 'Center text in the editor.',
47766             cls: 'x-html-editor-tip'
47767         },
47768         justifyright : {
47769             title: 'Align Text Right',
47770             text: 'Align text to the right.',
47771             cls: 'x-html-editor-tip'
47772         },
47773         insertunorderedlist : {
47774             title: 'Bullet List',
47775             text: 'Start a bulleted list.',
47776             cls: 'x-html-editor-tip'
47777         },
47778         insertorderedlist : {
47779             title: 'Numbered List',
47780             text: 'Start a numbered list.',
47781             cls: 'x-html-editor-tip'
47782         },
47783         createlink : {
47784             title: 'Hyperlink',
47785             text: 'Make the selected text a hyperlink.',
47786             cls: 'x-html-editor-tip'
47787         },
47788         sourceedit : {
47789             title: 'Source Edit',
47790             text: 'Switch to source editing mode.',
47791             cls: 'x-html-editor-tip'
47792         }
47793     },
47794     // private
47795     onDestroy : function(){
47796         if(this.rendered){
47797             
47798             this.tb.items.each(function(item){
47799                 if(item.menu){
47800                     item.menu.removeAll();
47801                     if(item.menu.el){
47802                         item.menu.el.destroy();
47803                     }
47804                 }
47805                 item.destroy();
47806             });
47807              
47808         }
47809     },
47810     onFirstFocus: function() {
47811         this.tb.items.each(function(item){
47812            item.enable();
47813         });
47814     }
47815 });
47816
47817
47818
47819
47820 // <script type="text/javascript">
47821 /*
47822  * Based on
47823  * Ext JS Library 1.1.1
47824  * Copyright(c) 2006-2007, Ext JS, LLC.
47825  *  
47826  
47827  */
47828
47829  
47830 /**
47831  * @class Roo.form.HtmlEditor.ToolbarContext
47832  * Context Toolbar
47833  * 
47834  * Usage:
47835  *
47836  new Roo.form.HtmlEditor({
47837     ....
47838     toolbars : [
47839         { xtype: 'ToolbarStandard', styles : {} }
47840         { xtype: 'ToolbarContext', disable : {} }
47841     ]
47842 })
47843
47844      
47845  * 
47846  * @config : {Object} disable List of elements to disable.. (not done yet.)
47847  * @config : {Object} styles  Map of styles available.
47848  * 
47849  */
47850
47851 Roo.form.HtmlEditor.ToolbarContext = function(config)
47852 {
47853     
47854     Roo.apply(this, config);
47855     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47856     // dont call parent... till later.
47857     this.styles = this.styles || {};
47858 }
47859
47860  
47861
47862 Roo.form.HtmlEditor.ToolbarContext.types = {
47863     'IMG' : {
47864         width : {
47865             title: "Width",
47866             width: 40
47867         },
47868         height:  {
47869             title: "Height",
47870             width: 40
47871         },
47872         align: {
47873             title: "Align",
47874             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
47875             width : 80
47876             
47877         },
47878         border: {
47879             title: "Border",
47880             width: 40
47881         },
47882         alt: {
47883             title: "Alt",
47884             width: 120
47885         },
47886         src : {
47887             title: "Src",
47888             width: 220
47889         }
47890         
47891     },
47892     'A' : {
47893         name : {
47894             title: "Name",
47895             width: 50
47896         },
47897         target:  {
47898             title: "Target",
47899             width: 120
47900         },
47901         href:  {
47902             title: "Href",
47903             width: 220
47904         } // border?
47905         
47906     },
47907     'TABLE' : {
47908         rows : {
47909             title: "Rows",
47910             width: 20
47911         },
47912         cols : {
47913             title: "Cols",
47914             width: 20
47915         },
47916         width : {
47917             title: "Width",
47918             width: 40
47919         },
47920         height : {
47921             title: "Height",
47922             width: 40
47923         },
47924         border : {
47925             title: "Border",
47926             width: 20
47927         }
47928     },
47929     'TD' : {
47930         width : {
47931             title: "Width",
47932             width: 40
47933         },
47934         height : {
47935             title: "Height",
47936             width: 40
47937         },   
47938         align: {
47939             title: "Align",
47940             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
47941             width: 80
47942         },
47943         valign: {
47944             title: "Valign",
47945             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
47946             width: 80
47947         },
47948         colspan: {
47949             title: "Colspan",
47950             width: 20
47951             
47952         },
47953          'font-family'  : {
47954             title : "Font",
47955             style : 'fontFamily',
47956             displayField: 'display',
47957             optname : 'font-family',
47958             width: 140
47959         }
47960     },
47961     'INPUT' : {
47962         name : {
47963             title: "name",
47964             width: 120
47965         },
47966         value : {
47967             title: "Value",
47968             width: 120
47969         },
47970         width : {
47971             title: "Width",
47972             width: 40
47973         }
47974     },
47975     'LABEL' : {
47976         'for' : {
47977             title: "For",
47978             width: 120
47979         }
47980     },
47981     'TEXTAREA' : {
47982           name : {
47983             title: "name",
47984             width: 120
47985         },
47986         rows : {
47987             title: "Rows",
47988             width: 20
47989         },
47990         cols : {
47991             title: "Cols",
47992             width: 20
47993         }
47994     },
47995     'SELECT' : {
47996         name : {
47997             title: "name",
47998             width: 120
47999         },
48000         selectoptions : {
48001             title: "Options",
48002             width: 200
48003         }
48004     },
48005     
48006     // should we really allow this??
48007     // should this just be 
48008     'BODY' : {
48009         title : {
48010             title: "Title",
48011             width: 200,
48012             disabled : true
48013         }
48014     },
48015     'SPAN' : {
48016         'font-family'  : {
48017             title : "Font",
48018             style : 'fontFamily',
48019             displayField: 'display',
48020             optname : 'font-family',
48021             width: 140
48022         }
48023     },
48024     'DIV' : {
48025         'font-family'  : {
48026             title : "Font",
48027             style : 'fontFamily',
48028             displayField: 'display',
48029             optname : 'font-family',
48030             width: 140
48031         }
48032     },
48033      'P' : {
48034         'font-family'  : {
48035             title : "Font",
48036             style : 'fontFamily',
48037             displayField: 'display',
48038             optname : 'font-family',
48039             width: 140
48040         }
48041     },
48042     
48043     '*' : {
48044         // empty..
48045     }
48046
48047 };
48048
48049 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
48050 Roo.form.HtmlEditor.ToolbarContext.stores = false;
48051
48052 Roo.form.HtmlEditor.ToolbarContext.options = {
48053         'font-family'  : [ 
48054                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
48055                 [ 'Courier New', 'Courier New'],
48056                 [ 'Tahoma', 'Tahoma'],
48057                 [ 'Times New Roman,serif', 'Times'],
48058                 [ 'Verdana','Verdana' ]
48059         ]
48060 };
48061
48062 // fixme - these need to be configurable..
48063  
48064
48065 //Roo.form.HtmlEditor.ToolbarContext.types
48066
48067
48068 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
48069     
48070     tb: false,
48071     
48072     rendered: false,
48073     
48074     editor : false,
48075     editorcore : false,
48076     /**
48077      * @cfg {Object} disable  List of toolbar elements to disable
48078          
48079      */
48080     disable : false,
48081     /**
48082      * @cfg {Object} styles List of styles 
48083      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
48084      *
48085      * These must be defined in the page, so they get rendered correctly..
48086      * .headline { }
48087      * TD.underline { }
48088      * 
48089      */
48090     styles : false,
48091     
48092     options: false,
48093     
48094     toolbars : false,
48095     
48096     init : function(editor)
48097     {
48098         this.editor = editor;
48099         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48100         var editorcore = this.editorcore;
48101         
48102         var fid = editorcore.frameId;
48103         var etb = this;
48104         function btn(id, toggle, handler){
48105             var xid = fid + '-'+ id ;
48106             return {
48107                 id : xid,
48108                 cmd : id,
48109                 cls : 'x-btn-icon x-edit-'+id,
48110                 enableToggle:toggle !== false,
48111                 scope: editorcore, // was editor...
48112                 handler:handler||editorcore.relayBtnCmd,
48113                 clickEvent:'mousedown',
48114                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48115                 tabIndex:-1
48116             };
48117         }
48118         // create a new element.
48119         var wdiv = editor.wrap.createChild({
48120                 tag: 'div'
48121             }, editor.wrap.dom.firstChild.nextSibling, true);
48122         
48123         // can we do this more than once??
48124         
48125          // stop form submits
48126       
48127  
48128         // disable everything...
48129         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48130         this.toolbars = {};
48131            
48132         for (var i in  ty) {
48133           
48134             this.toolbars[i] = this.buildToolbar(ty[i],i);
48135         }
48136         this.tb = this.toolbars.BODY;
48137         this.tb.el.show();
48138         this.buildFooter();
48139         this.footer.show();
48140         editor.on('hide', function( ) { this.footer.hide() }, this);
48141         editor.on('show', function( ) { this.footer.show() }, this);
48142         
48143          
48144         this.rendered = true;
48145         
48146         // the all the btns;
48147         editor.on('editorevent', this.updateToolbar, this);
48148         // other toolbars need to implement this..
48149         //editor.on('editmodechange', this.updateToolbar, this);
48150     },
48151     
48152     
48153     
48154     /**
48155      * Protected method that will not generally be called directly. It triggers
48156      * a toolbar update by reading the markup state of the current selection in the editor.
48157      *
48158      * Note you can force an update by calling on('editorevent', scope, false)
48159      */
48160     updateToolbar: function(editor,ev,sel){
48161
48162         //Roo.log(ev);
48163         // capture mouse up - this is handy for selecting images..
48164         // perhaps should go somewhere else...
48165         if(!this.editorcore.activated){
48166              this.editor.onFirstFocus();
48167             return;
48168         }
48169         
48170         
48171         
48172         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
48173         // selectNode - might want to handle IE?
48174         if (ev &&
48175             (ev.type == 'mouseup' || ev.type == 'click' ) &&
48176             ev.target && ev.target.tagName == 'IMG') {
48177             // they have click on an image...
48178             // let's see if we can change the selection...
48179             sel = ev.target;
48180          
48181               var nodeRange = sel.ownerDocument.createRange();
48182             try {
48183                 nodeRange.selectNode(sel);
48184             } catch (e) {
48185                 nodeRange.selectNodeContents(sel);
48186             }
48187             //nodeRange.collapse(true);
48188             var s = this.editorcore.win.getSelection();
48189             s.removeAllRanges();
48190             s.addRange(nodeRange);
48191         }  
48192         
48193       
48194         var updateFooter = sel ? false : true;
48195         
48196         
48197         var ans = this.editorcore.getAllAncestors();
48198         
48199         // pick
48200         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48201         
48202         if (!sel) { 
48203             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
48204             sel = sel ? sel : this.editorcore.doc.body;
48205             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
48206             
48207         }
48208         // pick a menu that exists..
48209         var tn = sel.tagName.toUpperCase();
48210         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
48211         
48212         tn = sel.tagName.toUpperCase();
48213         
48214         var lastSel = this.tb.selectedNode;
48215         
48216         this.tb.selectedNode = sel;
48217         
48218         // if current menu does not match..
48219         
48220         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
48221                 
48222             this.tb.el.hide();
48223             ///console.log("show: " + tn);
48224             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
48225             this.tb.el.show();
48226             // update name
48227             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
48228             
48229             
48230             // update attributes
48231             if (this.tb.fields) {
48232                 this.tb.fields.each(function(e) {
48233                     if (e.stylename) {
48234                         e.setValue(sel.style[e.stylename]);
48235                         return;
48236                     } 
48237                    e.setValue(sel.getAttribute(e.attrname));
48238                 });
48239             }
48240             
48241             var hasStyles = false;
48242             for(var i in this.styles) {
48243                 hasStyles = true;
48244                 break;
48245             }
48246             
48247             // update styles
48248             if (hasStyles) { 
48249                 var st = this.tb.fields.item(0);
48250                 
48251                 st.store.removeAll();
48252                
48253                 
48254                 var cn = sel.className.split(/\s+/);
48255                 
48256                 var avs = [];
48257                 if (this.styles['*']) {
48258                     
48259                     Roo.each(this.styles['*'], function(v) {
48260                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48261                     });
48262                 }
48263                 if (this.styles[tn]) { 
48264                     Roo.each(this.styles[tn], function(v) {
48265                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48266                     });
48267                 }
48268                 
48269                 st.store.loadData(avs);
48270                 st.collapse();
48271                 st.setValue(cn);
48272             }
48273             // flag our selected Node.
48274             this.tb.selectedNode = sel;
48275            
48276            
48277             Roo.menu.MenuMgr.hideAll();
48278
48279         }
48280         
48281         if (!updateFooter) {
48282             //this.footDisp.dom.innerHTML = ''; 
48283             return;
48284         }
48285         // update the footer
48286         //
48287         var html = '';
48288         
48289         this.footerEls = ans.reverse();
48290         Roo.each(this.footerEls, function(a,i) {
48291             if (!a) { return; }
48292             html += html.length ? ' &gt; '  :  '';
48293             
48294             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48295             
48296         });
48297        
48298         // 
48299         var sz = this.footDisp.up('td').getSize();
48300         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48301         this.footDisp.dom.style.marginLeft = '5px';
48302         
48303         this.footDisp.dom.style.overflow = 'hidden';
48304         
48305         this.footDisp.dom.innerHTML = html;
48306             
48307         //this.editorsyncValue();
48308     },
48309      
48310     
48311    
48312        
48313     // private
48314     onDestroy : function(){
48315         if(this.rendered){
48316             
48317             this.tb.items.each(function(item){
48318                 if(item.menu){
48319                     item.menu.removeAll();
48320                     if(item.menu.el){
48321                         item.menu.el.destroy();
48322                     }
48323                 }
48324                 item.destroy();
48325             });
48326              
48327         }
48328     },
48329     onFirstFocus: function() {
48330         // need to do this for all the toolbars..
48331         this.tb.items.each(function(item){
48332            item.enable();
48333         });
48334     },
48335     buildToolbar: function(tlist, nm)
48336     {
48337         var editor = this.editor;
48338         var editorcore = this.editorcore;
48339          // create a new element.
48340         var wdiv = editor.wrap.createChild({
48341                 tag: 'div'
48342             }, editor.wrap.dom.firstChild.nextSibling, true);
48343         
48344        
48345         var tb = new Roo.Toolbar(wdiv);
48346         // add the name..
48347         
48348         tb.add(nm+ ":&nbsp;");
48349         
48350         var styles = [];
48351         for(var i in this.styles) {
48352             styles.push(i);
48353         }
48354         
48355         // styles...
48356         if (styles && styles.length) {
48357             
48358             // this needs a multi-select checkbox...
48359             tb.addField( new Roo.form.ComboBox({
48360                 store: new Roo.data.SimpleStore({
48361                     id : 'val',
48362                     fields: ['val', 'selected'],
48363                     data : [] 
48364                 }),
48365                 name : '-roo-edit-className',
48366                 attrname : 'className',
48367                 displayField: 'val',
48368                 typeAhead: false,
48369                 mode: 'local',
48370                 editable : false,
48371                 triggerAction: 'all',
48372                 emptyText:'Select Style',
48373                 selectOnFocus:true,
48374                 width: 130,
48375                 listeners : {
48376                     'select': function(c, r, i) {
48377                         // initial support only for on class per el..
48378                         tb.selectedNode.className =  r ? r.get('val') : '';
48379                         editorcore.syncValue();
48380                     }
48381                 }
48382     
48383             }));
48384         }
48385         
48386         var tbc = Roo.form.HtmlEditor.ToolbarContext;
48387         var tbops = tbc.options;
48388         
48389         for (var i in tlist) {
48390             
48391             var item = tlist[i];
48392             tb.add(item.title + ":&nbsp;");
48393             
48394             
48395             //optname == used so you can configure the options available..
48396             var opts = item.opts ? item.opts : false;
48397             if (item.optname) {
48398                 opts = tbops[item.optname];
48399            
48400             }
48401             
48402             if (opts) {
48403                 // opts == pulldown..
48404                 tb.addField( new Roo.form.ComboBox({
48405                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
48406                         id : 'val',
48407                         fields: ['val', 'display'],
48408                         data : opts  
48409                     }),
48410                     name : '-roo-edit-' + i,
48411                     attrname : i,
48412                     stylename : item.style ? item.style : false,
48413                     displayField: item.displayField ? item.displayField : 'val',
48414                     valueField :  'val',
48415                     typeAhead: false,
48416                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
48417                     editable : false,
48418                     triggerAction: 'all',
48419                     emptyText:'Select',
48420                     selectOnFocus:true,
48421                     width: item.width ? item.width  : 130,
48422                     listeners : {
48423                         'select': function(c, r, i) {
48424                             if (c.stylename) {
48425                                 tb.selectedNode.style[c.stylename] =  r.get('val');
48426                                 return;
48427                             }
48428                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
48429                         }
48430                     }
48431
48432                 }));
48433                 continue;
48434                     
48435                  
48436                 
48437                 tb.addField( new Roo.form.TextField({
48438                     name: i,
48439                     width: 100,
48440                     //allowBlank:false,
48441                     value: ''
48442                 }));
48443                 continue;
48444             }
48445             tb.addField( new Roo.form.TextField({
48446                 name: '-roo-edit-' + i,
48447                 attrname : i,
48448                 
48449                 width: item.width,
48450                 //allowBlank:true,
48451                 value: '',
48452                 listeners: {
48453                     'change' : function(f, nv, ov) {
48454                         tb.selectedNode.setAttribute(f.attrname, nv);
48455                         editorcore.syncValue();
48456                     }
48457                 }
48458             }));
48459              
48460         }
48461         
48462         var _this = this;
48463         
48464         if(nm == 'BODY'){
48465             tb.addSeparator();
48466         
48467             tb.addButton( {
48468                 text: 'Stylesheets',
48469
48470                 listeners : {
48471                     click : function ()
48472                     {
48473                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
48474                     }
48475                 }
48476             });
48477         }
48478         
48479         tb.addFill();
48480         tb.addButton( {
48481             text: 'Remove Tag',
48482     
48483             listeners : {
48484                 click : function ()
48485                 {
48486                     // remove
48487                     // undo does not work.
48488                      
48489                     var sn = tb.selectedNode;
48490                     
48491                     var pn = sn.parentNode;
48492                     
48493                     var stn =  sn.childNodes[0];
48494                     var en = sn.childNodes[sn.childNodes.length - 1 ];
48495                     while (sn.childNodes.length) {
48496                         var node = sn.childNodes[0];
48497                         sn.removeChild(node);
48498                         //Roo.log(node);
48499                         pn.insertBefore(node, sn);
48500                         
48501                     }
48502                     pn.removeChild(sn);
48503                     var range = editorcore.createRange();
48504         
48505                     range.setStart(stn,0);
48506                     range.setEnd(en,0); //????
48507                     //range.selectNode(sel);
48508                     
48509                     
48510                     var selection = editorcore.getSelection();
48511                     selection.removeAllRanges();
48512                     selection.addRange(range);
48513                     
48514                     
48515                     
48516                     //_this.updateToolbar(null, null, pn);
48517                     _this.updateToolbar(null, null, null);
48518                     _this.footDisp.dom.innerHTML = ''; 
48519                 }
48520             }
48521             
48522                     
48523                 
48524             
48525         });
48526         
48527         
48528         tb.el.on('click', function(e){
48529             e.preventDefault(); // what does this do?
48530         });
48531         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
48532         tb.el.hide();
48533         tb.name = nm;
48534         // dont need to disable them... as they will get hidden
48535         return tb;
48536          
48537         
48538     },
48539     buildFooter : function()
48540     {
48541         
48542         var fel = this.editor.wrap.createChild();
48543         this.footer = new Roo.Toolbar(fel);
48544         // toolbar has scrolly on left / right?
48545         var footDisp= new Roo.Toolbar.Fill();
48546         var _t = this;
48547         this.footer.add(
48548             {
48549                 text : '&lt;',
48550                 xtype: 'Button',
48551                 handler : function() {
48552                     _t.footDisp.scrollTo('left',0,true)
48553                 }
48554             }
48555         );
48556         this.footer.add( footDisp );
48557         this.footer.add( 
48558             {
48559                 text : '&gt;',
48560                 xtype: 'Button',
48561                 handler : function() {
48562                     // no animation..
48563                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
48564                 }
48565             }
48566         );
48567         var fel = Roo.get(footDisp.el);
48568         fel.addClass('x-editor-context');
48569         this.footDispWrap = fel; 
48570         this.footDispWrap.overflow  = 'hidden';
48571         
48572         this.footDisp = fel.createChild();
48573         this.footDispWrap.on('click', this.onContextClick, this)
48574         
48575         
48576     },
48577     onContextClick : function (ev,dom)
48578     {
48579         ev.preventDefault();
48580         var  cn = dom.className;
48581         //Roo.log(cn);
48582         if (!cn.match(/x-ed-loc-/)) {
48583             return;
48584         }
48585         var n = cn.split('-').pop();
48586         var ans = this.footerEls;
48587         var sel = ans[n];
48588         
48589          // pick
48590         var range = this.editorcore.createRange();
48591         
48592         range.selectNodeContents(sel);
48593         //range.selectNode(sel);
48594         
48595         
48596         var selection = this.editorcore.getSelection();
48597         selection.removeAllRanges();
48598         selection.addRange(range);
48599         
48600         
48601         
48602         this.updateToolbar(null, null, sel);
48603         
48604         
48605     }
48606     
48607     
48608     
48609     
48610     
48611 });
48612
48613
48614
48615
48616
48617 /*
48618  * Based on:
48619  * Ext JS Library 1.1.1
48620  * Copyright(c) 2006-2007, Ext JS, LLC.
48621  *
48622  * Originally Released Under LGPL - original licence link has changed is not relivant.
48623  *
48624  * Fork - LGPL
48625  * <script type="text/javascript">
48626  */
48627  
48628 /**
48629  * @class Roo.form.BasicForm
48630  * @extends Roo.util.Observable
48631  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
48632  * @constructor
48633  * @param {String/HTMLElement/Roo.Element} el The form element or its id
48634  * @param {Object} config Configuration options
48635  */
48636 Roo.form.BasicForm = function(el, config){
48637     this.allItems = [];
48638     this.childForms = [];
48639     Roo.apply(this, config);
48640     /*
48641      * The Roo.form.Field items in this form.
48642      * @type MixedCollection
48643      */
48644      
48645      
48646     this.items = new Roo.util.MixedCollection(false, function(o){
48647         return o.id || (o.id = Roo.id());
48648     });
48649     this.addEvents({
48650         /**
48651          * @event beforeaction
48652          * Fires before any action is performed. Return false to cancel the action.
48653          * @param {Form} this
48654          * @param {Action} action The action to be performed
48655          */
48656         beforeaction: true,
48657         /**
48658          * @event actionfailed
48659          * Fires when an action fails.
48660          * @param {Form} this
48661          * @param {Action} action The action that failed
48662          */
48663         actionfailed : true,
48664         /**
48665          * @event actioncomplete
48666          * Fires when an action is completed.
48667          * @param {Form} this
48668          * @param {Action} action The action that completed
48669          */
48670         actioncomplete : true
48671     });
48672     if(el){
48673         this.initEl(el);
48674     }
48675     Roo.form.BasicForm.superclass.constructor.call(this);
48676     
48677     Roo.form.BasicForm.popover.apply();
48678 };
48679
48680 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
48681     /**
48682      * @cfg {String} method
48683      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
48684      */
48685     /**
48686      * @cfg {DataReader} reader
48687      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
48688      * This is optional as there is built-in support for processing JSON.
48689      */
48690     /**
48691      * @cfg {DataReader} errorReader
48692      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
48693      * This is completely optional as there is built-in support for processing JSON.
48694      */
48695     /**
48696      * @cfg {String} url
48697      * The URL to use for form actions if one isn't supplied in the action options.
48698      */
48699     /**
48700      * @cfg {Boolean} fileUpload
48701      * Set to true if this form is a file upload.
48702      */
48703      
48704     /**
48705      * @cfg {Object} baseParams
48706      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
48707      */
48708      /**
48709      
48710     /**
48711      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
48712      */
48713     timeout: 30,
48714
48715     // private
48716     activeAction : null,
48717
48718     /**
48719      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
48720      * or setValues() data instead of when the form was first created.
48721      */
48722     trackResetOnLoad : false,
48723     
48724     
48725     /**
48726      * childForms - used for multi-tab forms
48727      * @type {Array}
48728      */
48729     childForms : false,
48730     
48731     /**
48732      * allItems - full list of fields.
48733      * @type {Array}
48734      */
48735     allItems : false,
48736     
48737     /**
48738      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
48739      * element by passing it or its id or mask the form itself by passing in true.
48740      * @type Mixed
48741      */
48742     waitMsgTarget : false,
48743     
48744     /**
48745      * @type Boolean
48746      */
48747     disableMask : false,
48748     
48749     /**
48750      * @cfg {Boolean} errorMask (true|false) default false
48751      */
48752     errorMask : false,
48753     
48754     /**
48755      * @cfg {Number} maskOffset Default 100
48756      */
48757     maskOffset : 100,
48758
48759     // private
48760     initEl : function(el){
48761         this.el = Roo.get(el);
48762         this.id = this.el.id || Roo.id();
48763         this.el.on('submit', this.onSubmit, this);
48764         this.el.addClass('x-form');
48765     },
48766
48767     // private
48768     onSubmit : function(e){
48769         e.stopEvent();
48770     },
48771
48772     /**
48773      * Returns true if client-side validation on the form is successful.
48774      * @return Boolean
48775      */
48776     isValid : function(){
48777         var valid = true;
48778         var target = false;
48779         this.items.each(function(f){
48780             if(f.validate()){
48781                 return;
48782             }
48783             
48784             valid = false;
48785                 
48786             if(!target && f.el.isVisible(true)){
48787                 target = f;
48788             }
48789         });
48790         
48791         if(this.errorMask && !valid){
48792             Roo.form.BasicForm.popover.mask(this, target);
48793         }
48794         
48795         return valid;
48796     },
48797     /**
48798      * Returns array of invalid form fields.
48799      * @return Array
48800      */
48801     
48802     invalidFields : function()
48803     {
48804         var ret = [];
48805         this.items.each(function(f){
48806             if(f.validate()){
48807                 return;
48808             }
48809             ret.push(f);
48810             
48811         });
48812         
48813         return ret;
48814     },
48815     
48816     
48817     /**
48818      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
48819      * @return Boolean
48820      */
48821     isDirty : function(){
48822         var dirty = false;
48823         this.items.each(function(f){
48824            if(f.isDirty()){
48825                dirty = true;
48826                return false;
48827            }
48828         });
48829         return dirty;
48830     },
48831     
48832     /**
48833      * Returns true if any fields in this form have changed since their original load. (New version)
48834      * @return Boolean
48835      */
48836     
48837     hasChanged : function()
48838     {
48839         var dirty = false;
48840         this.items.each(function(f){
48841            if(f.hasChanged()){
48842                dirty = true;
48843                return false;
48844            }
48845         });
48846         return dirty;
48847         
48848     },
48849     /**
48850      * Resets all hasChanged to 'false' -
48851      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
48852      * So hasChanged storage is only to be used for this purpose
48853      * @return Boolean
48854      */
48855     resetHasChanged : function()
48856     {
48857         this.items.each(function(f){
48858            f.resetHasChanged();
48859         });
48860         
48861     },
48862     
48863     
48864     /**
48865      * Performs a predefined action (submit or load) or custom actions you define on this form.
48866      * @param {String} actionName The name of the action type
48867      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
48868      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
48869      * accept other config options):
48870      * <pre>
48871 Property          Type             Description
48872 ----------------  ---------------  ----------------------------------------------------------------------------------
48873 url               String           The url for the action (defaults to the form's url)
48874 method            String           The form method to use (defaults to the form's method, or POST if not defined)
48875 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
48876 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
48877                                    validate the form on the client (defaults to false)
48878      * </pre>
48879      * @return {BasicForm} this
48880      */
48881     doAction : function(action, options){
48882         if(typeof action == 'string'){
48883             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
48884         }
48885         if(this.fireEvent('beforeaction', this, action) !== false){
48886             this.beforeAction(action);
48887             action.run.defer(100, action);
48888         }
48889         return this;
48890     },
48891
48892     /**
48893      * Shortcut to do a submit action.
48894      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48895      * @return {BasicForm} this
48896      */
48897     submit : function(options){
48898         this.doAction('submit', options);
48899         return this;
48900     },
48901
48902     /**
48903      * Shortcut to do a load action.
48904      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48905      * @return {BasicForm} this
48906      */
48907     load : function(options){
48908         this.doAction('load', options);
48909         return this;
48910     },
48911
48912     /**
48913      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
48914      * @param {Record} record The record to edit
48915      * @return {BasicForm} this
48916      */
48917     updateRecord : function(record){
48918         record.beginEdit();
48919         var fs = record.fields;
48920         fs.each(function(f){
48921             var field = this.findField(f.name);
48922             if(field){
48923                 record.set(f.name, field.getValue());
48924             }
48925         }, this);
48926         record.endEdit();
48927         return this;
48928     },
48929
48930     /**
48931      * Loads an Roo.data.Record into this form.
48932      * @param {Record} record The record to load
48933      * @return {BasicForm} this
48934      */
48935     loadRecord : function(record){
48936         this.setValues(record.data);
48937         return this;
48938     },
48939
48940     // private
48941     beforeAction : function(action){
48942         var o = action.options;
48943         
48944         if(!this.disableMask) {
48945             if(this.waitMsgTarget === true){
48946                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
48947             }else if(this.waitMsgTarget){
48948                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
48949                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
48950             }else {
48951                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
48952             }
48953         }
48954         
48955          
48956     },
48957
48958     // private
48959     afterAction : function(action, success){
48960         this.activeAction = null;
48961         var o = action.options;
48962         
48963         if(!this.disableMask) {
48964             if(this.waitMsgTarget === true){
48965                 this.el.unmask();
48966             }else if(this.waitMsgTarget){
48967                 this.waitMsgTarget.unmask();
48968             }else{
48969                 Roo.MessageBox.updateProgress(1);
48970                 Roo.MessageBox.hide();
48971             }
48972         }
48973         
48974         if(success){
48975             if(o.reset){
48976                 this.reset();
48977             }
48978             Roo.callback(o.success, o.scope, [this, action]);
48979             this.fireEvent('actioncomplete', this, action);
48980             
48981         }else{
48982             
48983             // failure condition..
48984             // we have a scenario where updates need confirming.
48985             // eg. if a locking scenario exists..
48986             // we look for { errors : { needs_confirm : true }} in the response.
48987             if (
48988                 (typeof(action.result) != 'undefined')  &&
48989                 (typeof(action.result.errors) != 'undefined')  &&
48990                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48991            ){
48992                 var _t = this;
48993                 Roo.MessageBox.confirm(
48994                     "Change requires confirmation",
48995                     action.result.errorMsg,
48996                     function(r) {
48997                         if (r != 'yes') {
48998                             return;
48999                         }
49000                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
49001                     }
49002                     
49003                 );
49004                 
49005                 
49006                 
49007                 return;
49008             }
49009             
49010             Roo.callback(o.failure, o.scope, [this, action]);
49011             // show an error message if no failed handler is set..
49012             if (!this.hasListener('actionfailed')) {
49013                 Roo.MessageBox.alert("Error",
49014                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
49015                         action.result.errorMsg :
49016                         "Saving Failed, please check your entries or try again"
49017                 );
49018             }
49019             
49020             this.fireEvent('actionfailed', this, action);
49021         }
49022         
49023     },
49024
49025     /**
49026      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
49027      * @param {String} id The value to search for
49028      * @return Field
49029      */
49030     findField : function(id){
49031         var field = this.items.get(id);
49032         if(!field){
49033             this.items.each(function(f){
49034                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
49035                     field = f;
49036                     return false;
49037                 }
49038             });
49039         }
49040         return field || null;
49041     },
49042
49043     /**
49044      * Add a secondary form to this one, 
49045      * Used to provide tabbed forms. One form is primary, with hidden values 
49046      * which mirror the elements from the other forms.
49047      * 
49048      * @param {Roo.form.Form} form to add.
49049      * 
49050      */
49051     addForm : function(form)
49052     {
49053        
49054         if (this.childForms.indexOf(form) > -1) {
49055             // already added..
49056             return;
49057         }
49058         this.childForms.push(form);
49059         var n = '';
49060         Roo.each(form.allItems, function (fe) {
49061             
49062             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
49063             if (this.findField(n)) { // already added..
49064                 return;
49065             }
49066             var add = new Roo.form.Hidden({
49067                 name : n
49068             });
49069             add.render(this.el);
49070             
49071             this.add( add );
49072         }, this);
49073         
49074     },
49075     /**
49076      * Mark fields in this form invalid in bulk.
49077      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
49078      * @return {BasicForm} this
49079      */
49080     markInvalid : function(errors){
49081         if(errors instanceof Array){
49082             for(var i = 0, len = errors.length; i < len; i++){
49083                 var fieldError = errors[i];
49084                 var f = this.findField(fieldError.id);
49085                 if(f){
49086                     f.markInvalid(fieldError.msg);
49087                 }
49088             }
49089         }else{
49090             var field, id;
49091             for(id in errors){
49092                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
49093                     field.markInvalid(errors[id]);
49094                 }
49095             }
49096         }
49097         Roo.each(this.childForms || [], function (f) {
49098             f.markInvalid(errors);
49099         });
49100         
49101         return this;
49102     },
49103
49104     /**
49105      * Set values for fields in this form in bulk.
49106      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
49107      * @return {BasicForm} this
49108      */
49109     setValues : function(values){
49110         if(values instanceof Array){ // array of objects
49111             for(var i = 0, len = values.length; i < len; i++){
49112                 var v = values[i];
49113                 var f = this.findField(v.id);
49114                 if(f){
49115                     f.setValue(v.value);
49116                     if(this.trackResetOnLoad){
49117                         f.originalValue = f.getValue();
49118                     }
49119                 }
49120             }
49121         }else{ // object hash
49122             var field, id;
49123             for(id in values){
49124                 if(typeof values[id] != 'function' && (field = this.findField(id))){
49125                     
49126                     if (field.setFromData && 
49127                         field.valueField && 
49128                         field.displayField &&
49129                         // combos' with local stores can 
49130                         // be queried via setValue()
49131                         // to set their value..
49132                         (field.store && !field.store.isLocal)
49133                         ) {
49134                         // it's a combo
49135                         var sd = { };
49136                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
49137                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
49138                         field.setFromData(sd);
49139                         
49140                     } else {
49141                         field.setValue(values[id]);
49142                     }
49143                     
49144                     
49145                     if(this.trackResetOnLoad){
49146                         field.originalValue = field.getValue();
49147                     }
49148                 }
49149             }
49150         }
49151         this.resetHasChanged();
49152         
49153         
49154         Roo.each(this.childForms || [], function (f) {
49155             f.setValues(values);
49156             f.resetHasChanged();
49157         });
49158                 
49159         return this;
49160     },
49161  
49162     /**
49163      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
49164      * they are returned as an array.
49165      * @param {Boolean} asString
49166      * @return {Object}
49167      */
49168     getValues : function(asString){
49169         if (this.childForms) {
49170             // copy values from the child forms
49171             Roo.each(this.childForms, function (f) {
49172                 this.setValues(f.getValues());
49173             }, this);
49174         }
49175         
49176         // use formdata
49177         if (typeof(FormData) != 'undefined' && asString !== true) {
49178             // this relies on a 'recent' version of chrome apparently...
49179             try {
49180                 var fd = (new FormData(this.el.dom)).entries();
49181                 var ret = {};
49182                 var ent = fd.next();
49183                 while (!ent.done) {
49184                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
49185                     ent = fd.next();
49186                 };
49187                 return ret;
49188             } catch(e) {
49189                 
49190             }
49191             
49192         }
49193         
49194         
49195         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
49196         if(asString === true){
49197             return fs;
49198         }
49199         return Roo.urlDecode(fs);
49200     },
49201     
49202     /**
49203      * Returns the fields in this form as an object with key/value pairs. 
49204      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
49205      * @return {Object}
49206      */
49207     getFieldValues : function(with_hidden)
49208     {
49209         if (this.childForms) {
49210             // copy values from the child forms
49211             // should this call getFieldValues - probably not as we do not currently copy
49212             // hidden fields when we generate..
49213             Roo.each(this.childForms, function (f) {
49214                 this.setValues(f.getValues());
49215             }, this);
49216         }
49217         
49218         var ret = {};
49219         this.items.each(function(f){
49220             if (!f.getName()) {
49221                 return;
49222             }
49223             var v = f.getValue();
49224             if (f.inputType =='radio') {
49225                 if (typeof(ret[f.getName()]) == 'undefined') {
49226                     ret[f.getName()] = ''; // empty..
49227                 }
49228                 
49229                 if (!f.el.dom.checked) {
49230                     return;
49231                     
49232                 }
49233                 v = f.el.dom.value;
49234                 
49235             }
49236             
49237             // not sure if this supported any more..
49238             if ((typeof(v) == 'object') && f.getRawValue) {
49239                 v = f.getRawValue() ; // dates..
49240             }
49241             // combo boxes where name != hiddenName...
49242             if (f.name != f.getName()) {
49243                 ret[f.name] = f.getRawValue();
49244             }
49245             ret[f.getName()] = v;
49246         });
49247         
49248         return ret;
49249     },
49250
49251     /**
49252      * Clears all invalid messages in this form.
49253      * @return {BasicForm} this
49254      */
49255     clearInvalid : function(){
49256         this.items.each(function(f){
49257            f.clearInvalid();
49258         });
49259         
49260         Roo.each(this.childForms || [], function (f) {
49261             f.clearInvalid();
49262         });
49263         
49264         
49265         return this;
49266     },
49267
49268     /**
49269      * Resets this form.
49270      * @return {BasicForm} this
49271      */
49272     reset : function(){
49273         this.items.each(function(f){
49274             f.reset();
49275         });
49276         
49277         Roo.each(this.childForms || [], function (f) {
49278             f.reset();
49279         });
49280         this.resetHasChanged();
49281         
49282         return this;
49283     },
49284
49285     /**
49286      * Add Roo.form components to this form.
49287      * @param {Field} field1
49288      * @param {Field} field2 (optional)
49289      * @param {Field} etc (optional)
49290      * @return {BasicForm} this
49291      */
49292     add : function(){
49293         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49294         return this;
49295     },
49296
49297
49298     /**
49299      * Removes a field from the items collection (does NOT remove its markup).
49300      * @param {Field} field
49301      * @return {BasicForm} this
49302      */
49303     remove : function(field){
49304         this.items.remove(field);
49305         return this;
49306     },
49307
49308     /**
49309      * Looks at the fields in this form, checks them for an id attribute,
49310      * and calls applyTo on the existing dom element with that id.
49311      * @return {BasicForm} this
49312      */
49313     render : function(){
49314         this.items.each(function(f){
49315             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49316                 f.applyTo(f.id);
49317             }
49318         });
49319         return this;
49320     },
49321
49322     /**
49323      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49324      * @param {Object} values
49325      * @return {BasicForm} this
49326      */
49327     applyToFields : function(o){
49328         this.items.each(function(f){
49329            Roo.apply(f, o);
49330         });
49331         return this;
49332     },
49333
49334     /**
49335      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49336      * @param {Object} values
49337      * @return {BasicForm} this
49338      */
49339     applyIfToFields : function(o){
49340         this.items.each(function(f){
49341            Roo.applyIf(f, o);
49342         });
49343         return this;
49344     }
49345 });
49346
49347 // back compat
49348 Roo.BasicForm = Roo.form.BasicForm;
49349
49350 Roo.apply(Roo.form.BasicForm, {
49351     
49352     popover : {
49353         
49354         padding : 5,
49355         
49356         isApplied : false,
49357         
49358         isMasked : false,
49359         
49360         form : false,
49361         
49362         target : false,
49363         
49364         intervalID : false,
49365         
49366         maskEl : false,
49367         
49368         apply : function()
49369         {
49370             if(this.isApplied){
49371                 return;
49372             }
49373             
49374             this.maskEl = {
49375                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
49376                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
49377                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
49378                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
49379             };
49380             
49381             this.maskEl.top.enableDisplayMode("block");
49382             this.maskEl.left.enableDisplayMode("block");
49383             this.maskEl.bottom.enableDisplayMode("block");
49384             this.maskEl.right.enableDisplayMode("block");
49385             
49386             Roo.get(document.body).on('click', function(){
49387                 this.unmask();
49388             }, this);
49389             
49390             Roo.get(document.body).on('touchstart', function(){
49391                 this.unmask();
49392             }, this);
49393             
49394             this.isApplied = true
49395         },
49396         
49397         mask : function(form, target)
49398         {
49399             this.form = form;
49400             
49401             this.target = target;
49402             
49403             if(!this.form.errorMask || !target.el){
49404                 return;
49405             }
49406             
49407             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
49408             
49409             var ot = this.target.el.calcOffsetsTo(scrollable);
49410             
49411             var scrollTo = ot[1] - this.form.maskOffset;
49412             
49413             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
49414             
49415             scrollable.scrollTo('top', scrollTo);
49416             
49417             var el = this.target.wrap || this.target.el;
49418             
49419             var box = el.getBox();
49420             
49421             this.maskEl.top.setStyle('position', 'absolute');
49422             this.maskEl.top.setStyle('z-index', 10000);
49423             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
49424             this.maskEl.top.setLeft(0);
49425             this.maskEl.top.setTop(0);
49426             this.maskEl.top.show();
49427             
49428             this.maskEl.left.setStyle('position', 'absolute');
49429             this.maskEl.left.setStyle('z-index', 10000);
49430             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
49431             this.maskEl.left.setLeft(0);
49432             this.maskEl.left.setTop(box.y - this.padding);
49433             this.maskEl.left.show();
49434
49435             this.maskEl.bottom.setStyle('position', 'absolute');
49436             this.maskEl.bottom.setStyle('z-index', 10000);
49437             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
49438             this.maskEl.bottom.setLeft(0);
49439             this.maskEl.bottom.setTop(box.bottom + this.padding);
49440             this.maskEl.bottom.show();
49441
49442             this.maskEl.right.setStyle('position', 'absolute');
49443             this.maskEl.right.setStyle('z-index', 10000);
49444             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
49445             this.maskEl.right.setLeft(box.right + this.padding);
49446             this.maskEl.right.setTop(box.y - this.padding);
49447             this.maskEl.right.show();
49448
49449             this.intervalID = window.setInterval(function() {
49450                 Roo.form.BasicForm.popover.unmask();
49451             }, 10000);
49452
49453             window.onwheel = function(){ return false;};
49454             
49455             (function(){ this.isMasked = true; }).defer(500, this);
49456             
49457         },
49458         
49459         unmask : function()
49460         {
49461             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
49462                 return;
49463             }
49464             
49465             this.maskEl.top.setStyle('position', 'absolute');
49466             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
49467             this.maskEl.top.hide();
49468
49469             this.maskEl.left.setStyle('position', 'absolute');
49470             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
49471             this.maskEl.left.hide();
49472
49473             this.maskEl.bottom.setStyle('position', 'absolute');
49474             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
49475             this.maskEl.bottom.hide();
49476
49477             this.maskEl.right.setStyle('position', 'absolute');
49478             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
49479             this.maskEl.right.hide();
49480             
49481             window.onwheel = function(){ return true;};
49482             
49483             if(this.intervalID){
49484                 window.clearInterval(this.intervalID);
49485                 this.intervalID = false;
49486             }
49487             
49488             this.isMasked = false;
49489             
49490         }
49491         
49492     }
49493     
49494 });/*
49495  * Based on:
49496  * Ext JS Library 1.1.1
49497  * Copyright(c) 2006-2007, Ext JS, LLC.
49498  *
49499  * Originally Released Under LGPL - original licence link has changed is not relivant.
49500  *
49501  * Fork - LGPL
49502  * <script type="text/javascript">
49503  */
49504
49505 /**
49506  * @class Roo.form.Form
49507  * @extends Roo.form.BasicForm
49508  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49509  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
49510  * @constructor
49511  * @param {Object} config Configuration options
49512  */
49513 Roo.form.Form = function(config){
49514     var xitems =  [];
49515     if (config.items) {
49516         xitems = config.items;
49517         delete config.items;
49518     }
49519    
49520     
49521     Roo.form.Form.superclass.constructor.call(this, null, config);
49522     this.url = this.url || this.action;
49523     if(!this.root){
49524         this.root = new Roo.form.Layout(Roo.applyIf({
49525             id: Roo.id()
49526         }, config));
49527     }
49528     this.active = this.root;
49529     /**
49530      * Array of all the buttons that have been added to this form via {@link addButton}
49531      * @type Array
49532      */
49533     this.buttons = [];
49534     this.allItems = [];
49535     this.addEvents({
49536         /**
49537          * @event clientvalidation
49538          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
49539          * @param {Form} this
49540          * @param {Boolean} valid true if the form has passed client-side validation
49541          */
49542         clientvalidation: true,
49543         /**
49544          * @event rendered
49545          * Fires when the form is rendered
49546          * @param {Roo.form.Form} form
49547          */
49548         rendered : true
49549     });
49550     
49551     if (this.progressUrl) {
49552             // push a hidden field onto the list of fields..
49553             this.addxtype( {
49554                     xns: Roo.form, 
49555                     xtype : 'Hidden', 
49556                     name : 'UPLOAD_IDENTIFIER' 
49557             });
49558         }
49559         
49560     
49561     Roo.each(xitems, this.addxtype, this);
49562     
49563 };
49564
49565 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
49566      /**
49567      * @cfg {Roo.Button} buttons[] buttons at bottom of form
49568      */
49569     
49570     /**
49571      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
49572      */
49573     /**
49574      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
49575      */
49576     /**
49577      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
49578      */
49579     buttonAlign:'center',
49580
49581     /**
49582      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
49583      */
49584     minButtonWidth:75,
49585
49586     /**
49587      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
49588      * This property cascades to child containers if not set.
49589      */
49590     labelAlign:'left',
49591
49592     /**
49593      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
49594      * fires a looping event with that state. This is required to bind buttons to the valid
49595      * state using the config value formBind:true on the button.
49596      */
49597     monitorValid : false,
49598
49599     /**
49600      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
49601      */
49602     monitorPoll : 200,
49603     
49604     /**
49605      * @cfg {String} progressUrl - Url to return progress data 
49606      */
49607     
49608     progressUrl : false,
49609     /**
49610      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
49611      * sending a formdata with extra parameters - eg uploaded elements.
49612      */
49613     
49614     formData : false,
49615     
49616     /**
49617      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
49618      * fields are added and the column is closed. If no fields are passed the column remains open
49619      * until end() is called.
49620      * @param {Object} config The config to pass to the column
49621      * @param {Field} field1 (optional)
49622      * @param {Field} field2 (optional)
49623      * @param {Field} etc (optional)
49624      * @return Column The column container object
49625      */
49626     column : function(c){
49627         var col = new Roo.form.Column(c);
49628         this.start(col);
49629         if(arguments.length > 1){ // duplicate code required because of Opera
49630             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49631             this.end();
49632         }
49633         return col;
49634     },
49635
49636     /**
49637      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
49638      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
49639      * until end() is called.
49640      * @param {Object} config The config to pass to the fieldset
49641      * @param {Field} field1 (optional)
49642      * @param {Field} field2 (optional)
49643      * @param {Field} etc (optional)
49644      * @return FieldSet The fieldset container object
49645      */
49646     fieldset : function(c){
49647         var fs = new Roo.form.FieldSet(c);
49648         this.start(fs);
49649         if(arguments.length > 1){ // duplicate code required because of Opera
49650             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49651             this.end();
49652         }
49653         return fs;
49654     },
49655
49656     /**
49657      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
49658      * fields are added and the container is closed. If no fields are passed the container remains open
49659      * until end() is called.
49660      * @param {Object} config The config to pass to the Layout
49661      * @param {Field} field1 (optional)
49662      * @param {Field} field2 (optional)
49663      * @param {Field} etc (optional)
49664      * @return Layout The container object
49665      */
49666     container : function(c){
49667         var l = new Roo.form.Layout(c);
49668         this.start(l);
49669         if(arguments.length > 1){ // duplicate code required because of Opera
49670             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49671             this.end();
49672         }
49673         return l;
49674     },
49675
49676     /**
49677      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
49678      * @param {Object} container A Roo.form.Layout or subclass of Layout
49679      * @return {Form} this
49680      */
49681     start : function(c){
49682         // cascade label info
49683         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
49684         this.active.stack.push(c);
49685         c.ownerCt = this.active;
49686         this.active = c;
49687         return this;
49688     },
49689
49690     /**
49691      * Closes the current open container
49692      * @return {Form} this
49693      */
49694     end : function(){
49695         if(this.active == this.root){
49696             return this;
49697         }
49698         this.active = this.active.ownerCt;
49699         return this;
49700     },
49701
49702     /**
49703      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
49704      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
49705      * as the label of the field.
49706      * @param {Field} field1
49707      * @param {Field} field2 (optional)
49708      * @param {Field} etc. (optional)
49709      * @return {Form} this
49710      */
49711     add : function(){
49712         this.active.stack.push.apply(this.active.stack, arguments);
49713         this.allItems.push.apply(this.allItems,arguments);
49714         var r = [];
49715         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
49716             if(a[i].isFormField){
49717                 r.push(a[i]);
49718             }
49719         }
49720         if(r.length > 0){
49721             Roo.form.Form.superclass.add.apply(this, r);
49722         }
49723         return this;
49724     },
49725     
49726
49727     
49728     
49729     
49730      /**
49731      * Find any element that has been added to a form, using it's ID or name
49732      * This can include framesets, columns etc. along with regular fields..
49733      * @param {String} id - id or name to find.
49734      
49735      * @return {Element} e - or false if nothing found.
49736      */
49737     findbyId : function(id)
49738     {
49739         var ret = false;
49740         if (!id) {
49741             return ret;
49742         }
49743         Roo.each(this.allItems, function(f){
49744             if (f.id == id || f.name == id ){
49745                 ret = f;
49746                 return false;
49747             }
49748         });
49749         return ret;
49750     },
49751
49752     
49753     
49754     /**
49755      * Render this form into the passed container. This should only be called once!
49756      * @param {String/HTMLElement/Element} container The element this component should be rendered into
49757      * @return {Form} this
49758      */
49759     render : function(ct)
49760     {
49761         
49762         
49763         
49764         ct = Roo.get(ct);
49765         var o = this.autoCreate || {
49766             tag: 'form',
49767             method : this.method || 'POST',
49768             id : this.id || Roo.id()
49769         };
49770         this.initEl(ct.createChild(o));
49771
49772         this.root.render(this.el);
49773         
49774        
49775              
49776         this.items.each(function(f){
49777             f.render('x-form-el-'+f.id);
49778         });
49779
49780         if(this.buttons.length > 0){
49781             // tables are required to maintain order and for correct IE layout
49782             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
49783                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
49784                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
49785             }}, null, true);
49786             var tr = tb.getElementsByTagName('tr')[0];
49787             for(var i = 0, len = this.buttons.length; i < len; i++) {
49788                 var b = this.buttons[i];
49789                 var td = document.createElement('td');
49790                 td.className = 'x-form-btn-td';
49791                 b.render(tr.appendChild(td));
49792             }
49793         }
49794         if(this.monitorValid){ // initialize after render
49795             this.startMonitoring();
49796         }
49797         this.fireEvent('rendered', this);
49798         return this;
49799     },
49800
49801     /**
49802      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
49803      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
49804      * object or a valid Roo.DomHelper element config
49805      * @param {Function} handler The function called when the button is clicked
49806      * @param {Object} scope (optional) The scope of the handler function
49807      * @return {Roo.Button}
49808      */
49809     addButton : function(config, handler, scope){
49810         var bc = {
49811             handler: handler,
49812             scope: scope,
49813             minWidth: this.minButtonWidth,
49814             hideParent:true
49815         };
49816         if(typeof config == "string"){
49817             bc.text = config;
49818         }else{
49819             Roo.apply(bc, config);
49820         }
49821         var btn = new Roo.Button(null, bc);
49822         this.buttons.push(btn);
49823         return btn;
49824     },
49825
49826      /**
49827      * Adds a series of form elements (using the xtype property as the factory method.
49828      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
49829      * @param {Object} config 
49830      */
49831     
49832     addxtype : function()
49833     {
49834         var ar = Array.prototype.slice.call(arguments, 0);
49835         var ret = false;
49836         for(var i = 0; i < ar.length; i++) {
49837             if (!ar[i]) {
49838                 continue; // skip -- if this happends something invalid got sent, we 
49839                 // should ignore it, as basically that interface element will not show up
49840                 // and that should be pretty obvious!!
49841             }
49842             
49843             if (Roo.form[ar[i].xtype]) {
49844                 ar[i].form = this;
49845                 var fe = Roo.factory(ar[i], Roo.form);
49846                 if (!ret) {
49847                     ret = fe;
49848                 }
49849                 fe.form = this;
49850                 if (fe.store) {
49851                     fe.store.form = this;
49852                 }
49853                 if (fe.isLayout) {  
49854                          
49855                     this.start(fe);
49856                     this.allItems.push(fe);
49857                     if (fe.items && fe.addxtype) {
49858                         fe.addxtype.apply(fe, fe.items);
49859                         delete fe.items;
49860                     }
49861                      this.end();
49862                     continue;
49863                 }
49864                 
49865                 
49866                  
49867                 this.add(fe);
49868               //  console.log('adding ' + ar[i].xtype);
49869             }
49870             if (ar[i].xtype == 'Button') {  
49871                 //console.log('adding button');
49872                 //console.log(ar[i]);
49873                 this.addButton(ar[i]);
49874                 this.allItems.push(fe);
49875                 continue;
49876             }
49877             
49878             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
49879                 alert('end is not supported on xtype any more, use items');
49880             //    this.end();
49881             //    //console.log('adding end');
49882             }
49883             
49884         }
49885         return ret;
49886     },
49887     
49888     /**
49889      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
49890      * option "monitorValid"
49891      */
49892     startMonitoring : function(){
49893         if(!this.bound){
49894             this.bound = true;
49895             Roo.TaskMgr.start({
49896                 run : this.bindHandler,
49897                 interval : this.monitorPoll || 200,
49898                 scope: this
49899             });
49900         }
49901     },
49902
49903     /**
49904      * Stops monitoring of the valid state of this form
49905      */
49906     stopMonitoring : function(){
49907         this.bound = false;
49908     },
49909
49910     // private
49911     bindHandler : function(){
49912         if(!this.bound){
49913             return false; // stops binding
49914         }
49915         var valid = true;
49916         this.items.each(function(f){
49917             if(!f.isValid(true)){
49918                 valid = false;
49919                 return false;
49920             }
49921         });
49922         for(var i = 0, len = this.buttons.length; i < len; i++){
49923             var btn = this.buttons[i];
49924             if(btn.formBind === true && btn.disabled === valid){
49925                 btn.setDisabled(!valid);
49926             }
49927         }
49928         this.fireEvent('clientvalidation', this, valid);
49929     }
49930     
49931     
49932     
49933     
49934     
49935     
49936     
49937     
49938 });
49939
49940
49941 // back compat
49942 Roo.Form = Roo.form.Form;
49943 /*
49944  * Based on:
49945  * Ext JS Library 1.1.1
49946  * Copyright(c) 2006-2007, Ext JS, LLC.
49947  *
49948  * Originally Released Under LGPL - original licence link has changed is not relivant.
49949  *
49950  * Fork - LGPL
49951  * <script type="text/javascript">
49952  */
49953
49954 // as we use this in bootstrap.
49955 Roo.namespace('Roo.form');
49956  /**
49957  * @class Roo.form.Action
49958  * Internal Class used to handle form actions
49959  * @constructor
49960  * @param {Roo.form.BasicForm} el The form element or its id
49961  * @param {Object} config Configuration options
49962  */
49963
49964  
49965  
49966 // define the action interface
49967 Roo.form.Action = function(form, options){
49968     this.form = form;
49969     this.options = options || {};
49970 };
49971 /**
49972  * Client Validation Failed
49973  * @const 
49974  */
49975 Roo.form.Action.CLIENT_INVALID = 'client';
49976 /**
49977  * Server Validation Failed
49978  * @const 
49979  */
49980 Roo.form.Action.SERVER_INVALID = 'server';
49981  /**
49982  * Connect to Server Failed
49983  * @const 
49984  */
49985 Roo.form.Action.CONNECT_FAILURE = 'connect';
49986 /**
49987  * Reading Data from Server Failed
49988  * @const 
49989  */
49990 Roo.form.Action.LOAD_FAILURE = 'load';
49991
49992 Roo.form.Action.prototype = {
49993     type : 'default',
49994     failureType : undefined,
49995     response : undefined,
49996     result : undefined,
49997
49998     // interface method
49999     run : function(options){
50000
50001     },
50002
50003     // interface method
50004     success : function(response){
50005
50006     },
50007
50008     // interface method
50009     handleResponse : function(response){
50010
50011     },
50012
50013     // default connection failure
50014     failure : function(response){
50015         
50016         this.response = response;
50017         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50018         this.form.afterAction(this, false);
50019     },
50020
50021     processResponse : function(response){
50022         this.response = response;
50023         if(!response.responseText){
50024             return true;
50025         }
50026         this.result = this.handleResponse(response);
50027         return this.result;
50028     },
50029
50030     // utility functions used internally
50031     getUrl : function(appendParams){
50032         var url = this.options.url || this.form.url || this.form.el.dom.action;
50033         if(appendParams){
50034             var p = this.getParams();
50035             if(p){
50036                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
50037             }
50038         }
50039         return url;
50040     },
50041
50042     getMethod : function(){
50043         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
50044     },
50045
50046     getParams : function(){
50047         var bp = this.form.baseParams;
50048         var p = this.options.params;
50049         if(p){
50050             if(typeof p == "object"){
50051                 p = Roo.urlEncode(Roo.applyIf(p, bp));
50052             }else if(typeof p == 'string' && bp){
50053                 p += '&' + Roo.urlEncode(bp);
50054             }
50055         }else if(bp){
50056             p = Roo.urlEncode(bp);
50057         }
50058         return p;
50059     },
50060
50061     createCallback : function(){
50062         return {
50063             success: this.success,
50064             failure: this.failure,
50065             scope: this,
50066             timeout: (this.form.timeout*1000),
50067             upload: this.form.fileUpload ? this.success : undefined
50068         };
50069     }
50070 };
50071
50072 Roo.form.Action.Submit = function(form, options){
50073     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
50074 };
50075
50076 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
50077     type : 'submit',
50078
50079     haveProgress : false,
50080     uploadComplete : false,
50081     
50082     // uploadProgress indicator.
50083     uploadProgress : function()
50084     {
50085         if (!this.form.progressUrl) {
50086             return;
50087         }
50088         
50089         if (!this.haveProgress) {
50090             Roo.MessageBox.progress("Uploading", "Uploading");
50091         }
50092         if (this.uploadComplete) {
50093            Roo.MessageBox.hide();
50094            return;
50095         }
50096         
50097         this.haveProgress = true;
50098    
50099         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
50100         
50101         var c = new Roo.data.Connection();
50102         c.request({
50103             url : this.form.progressUrl,
50104             params: {
50105                 id : uid
50106             },
50107             method: 'GET',
50108             success : function(req){
50109                //console.log(data);
50110                 var rdata = false;
50111                 var edata;
50112                 try  {
50113                    rdata = Roo.decode(req.responseText)
50114                 } catch (e) {
50115                     Roo.log("Invalid data from server..");
50116                     Roo.log(edata);
50117                     return;
50118                 }
50119                 if (!rdata || !rdata.success) {
50120                     Roo.log(rdata);
50121                     Roo.MessageBox.alert(Roo.encode(rdata));
50122                     return;
50123                 }
50124                 var data = rdata.data;
50125                 
50126                 if (this.uploadComplete) {
50127                    Roo.MessageBox.hide();
50128                    return;
50129                 }
50130                    
50131                 if (data){
50132                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
50133                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
50134                     );
50135                 }
50136                 this.uploadProgress.defer(2000,this);
50137             },
50138        
50139             failure: function(data) {
50140                 Roo.log('progress url failed ');
50141                 Roo.log(data);
50142             },
50143             scope : this
50144         });
50145            
50146     },
50147     
50148     
50149     run : function()
50150     {
50151         // run get Values on the form, so it syncs any secondary forms.
50152         this.form.getValues();
50153         
50154         var o = this.options;
50155         var method = this.getMethod();
50156         var isPost = method == 'POST';
50157         if(o.clientValidation === false || this.form.isValid()){
50158             
50159             if (this.form.progressUrl) {
50160                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
50161                     (new Date() * 1) + '' + Math.random());
50162                     
50163             } 
50164             
50165             
50166             Roo.Ajax.request(Roo.apply(this.createCallback(), {
50167                 form:this.form.el.dom,
50168                 url:this.getUrl(!isPost),
50169                 method: method,
50170                 params:isPost ? this.getParams() : null,
50171                 isUpload: this.form.fileUpload,
50172                 formData : this.form.formData
50173             }));
50174             
50175             this.uploadProgress();
50176
50177         }else if (o.clientValidation !== false){ // client validation failed
50178             this.failureType = Roo.form.Action.CLIENT_INVALID;
50179             this.form.afterAction(this, false);
50180         }
50181     },
50182
50183     success : function(response)
50184     {
50185         this.uploadComplete= true;
50186         if (this.haveProgress) {
50187             Roo.MessageBox.hide();
50188         }
50189         
50190         
50191         var result = this.processResponse(response);
50192         if(result === true || result.success){
50193             this.form.afterAction(this, true);
50194             return;
50195         }
50196         if(result.errors){
50197             this.form.markInvalid(result.errors);
50198             this.failureType = Roo.form.Action.SERVER_INVALID;
50199         }
50200         this.form.afterAction(this, false);
50201     },
50202     failure : function(response)
50203     {
50204         this.uploadComplete= true;
50205         if (this.haveProgress) {
50206             Roo.MessageBox.hide();
50207         }
50208         
50209         this.response = response;
50210         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50211         this.form.afterAction(this, false);
50212     },
50213     
50214     handleResponse : function(response){
50215         if(this.form.errorReader){
50216             var rs = this.form.errorReader.read(response);
50217             var errors = [];
50218             if(rs.records){
50219                 for(var i = 0, len = rs.records.length; i < len; i++) {
50220                     var r = rs.records[i];
50221                     errors[i] = r.data;
50222                 }
50223             }
50224             if(errors.length < 1){
50225                 errors = null;
50226             }
50227             return {
50228                 success : rs.success,
50229                 errors : errors
50230             };
50231         }
50232         var ret = false;
50233         try {
50234             ret = Roo.decode(response.responseText);
50235         } catch (e) {
50236             ret = {
50237                 success: false,
50238                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
50239                 errors : []
50240             };
50241         }
50242         return ret;
50243         
50244     }
50245 });
50246
50247
50248 Roo.form.Action.Load = function(form, options){
50249     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
50250     this.reader = this.form.reader;
50251 };
50252
50253 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
50254     type : 'load',
50255
50256     run : function(){
50257         
50258         Roo.Ajax.request(Roo.apply(
50259                 this.createCallback(), {
50260                     method:this.getMethod(),
50261                     url:this.getUrl(false),
50262                     params:this.getParams()
50263         }));
50264     },
50265
50266     success : function(response){
50267         
50268         var result = this.processResponse(response);
50269         if(result === true || !result.success || !result.data){
50270             this.failureType = Roo.form.Action.LOAD_FAILURE;
50271             this.form.afterAction(this, false);
50272             return;
50273         }
50274         this.form.clearInvalid();
50275         this.form.setValues(result.data);
50276         this.form.afterAction(this, true);
50277     },
50278
50279     handleResponse : function(response){
50280         if(this.form.reader){
50281             var rs = this.form.reader.read(response);
50282             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50283             return {
50284                 success : rs.success,
50285                 data : data
50286             };
50287         }
50288         return Roo.decode(response.responseText);
50289     }
50290 });
50291
50292 Roo.form.Action.ACTION_TYPES = {
50293     'load' : Roo.form.Action.Load,
50294     'submit' : Roo.form.Action.Submit
50295 };/*
50296  * Based on:
50297  * Ext JS Library 1.1.1
50298  * Copyright(c) 2006-2007, Ext JS, LLC.
50299  *
50300  * Originally Released Under LGPL - original licence link has changed is not relivant.
50301  *
50302  * Fork - LGPL
50303  * <script type="text/javascript">
50304  */
50305  
50306 /**
50307  * @class Roo.form.Layout
50308  * @extends Roo.Component
50309  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50310  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50311  * @constructor
50312  * @param {Object} config Configuration options
50313  */
50314 Roo.form.Layout = function(config){
50315     var xitems = [];
50316     if (config.items) {
50317         xitems = config.items;
50318         delete config.items;
50319     }
50320     Roo.form.Layout.superclass.constructor.call(this, config);
50321     this.stack = [];
50322     Roo.each(xitems, this.addxtype, this);
50323      
50324 };
50325
50326 Roo.extend(Roo.form.Layout, Roo.Component, {
50327     /**
50328      * @cfg {String/Object} autoCreate
50329      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50330      */
50331     /**
50332      * @cfg {String/Object/Function} style
50333      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50334      * a function which returns such a specification.
50335      */
50336     /**
50337      * @cfg {String} labelAlign
50338      * Valid values are "left," "top" and "right" (defaults to "left")
50339      */
50340     /**
50341      * @cfg {Number} labelWidth
50342      * Fixed width in pixels of all field labels (defaults to undefined)
50343      */
50344     /**
50345      * @cfg {Boolean} clear
50346      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
50347      */
50348     clear : true,
50349     /**
50350      * @cfg {String} labelSeparator
50351      * The separator to use after field labels (defaults to ':')
50352      */
50353     labelSeparator : ':',
50354     /**
50355      * @cfg {Boolean} hideLabels
50356      * True to suppress the display of field labels in this layout (defaults to false)
50357      */
50358     hideLabels : false,
50359
50360     // private
50361     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
50362     
50363     isLayout : true,
50364     
50365     // private
50366     onRender : function(ct, position){
50367         if(this.el){ // from markup
50368             this.el = Roo.get(this.el);
50369         }else {  // generate
50370             var cfg = this.getAutoCreate();
50371             this.el = ct.createChild(cfg, position);
50372         }
50373         if(this.style){
50374             this.el.applyStyles(this.style);
50375         }
50376         if(this.labelAlign){
50377             this.el.addClass('x-form-label-'+this.labelAlign);
50378         }
50379         if(this.hideLabels){
50380             this.labelStyle = "display:none";
50381             this.elementStyle = "padding-left:0;";
50382         }else{
50383             if(typeof this.labelWidth == 'number'){
50384                 this.labelStyle = "width:"+this.labelWidth+"px;";
50385                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
50386             }
50387             if(this.labelAlign == 'top'){
50388                 this.labelStyle = "width:auto;";
50389                 this.elementStyle = "padding-left:0;";
50390             }
50391         }
50392         var stack = this.stack;
50393         var slen = stack.length;
50394         if(slen > 0){
50395             if(!this.fieldTpl){
50396                 var t = new Roo.Template(
50397                     '<div class="x-form-item {5}">',
50398                         '<label for="{0}" style="{2}">{1}{4}</label>',
50399                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50400                         '</div>',
50401                     '</div><div class="x-form-clear-left"></div>'
50402                 );
50403                 t.disableFormats = true;
50404                 t.compile();
50405                 Roo.form.Layout.prototype.fieldTpl = t;
50406             }
50407             for(var i = 0; i < slen; i++) {
50408                 if(stack[i].isFormField){
50409                     this.renderField(stack[i]);
50410                 }else{
50411                     this.renderComponent(stack[i]);
50412                 }
50413             }
50414         }
50415         if(this.clear){
50416             this.el.createChild({cls:'x-form-clear'});
50417         }
50418     },
50419
50420     // private
50421     renderField : function(f){
50422         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
50423                f.id, //0
50424                f.fieldLabel, //1
50425                f.labelStyle||this.labelStyle||'', //2
50426                this.elementStyle||'', //3
50427                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
50428                f.itemCls||this.itemCls||''  //5
50429        ], true).getPrevSibling());
50430     },
50431
50432     // private
50433     renderComponent : function(c){
50434         c.render(c.isLayout ? this.el : this.el.createChild());    
50435     },
50436     /**
50437      * Adds a object form elements (using the xtype property as the factory method.)
50438      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
50439      * @param {Object} config 
50440      */
50441     addxtype : function(o)
50442     {
50443         // create the lement.
50444         o.form = this.form;
50445         var fe = Roo.factory(o, Roo.form);
50446         this.form.allItems.push(fe);
50447         this.stack.push(fe);
50448         
50449         if (fe.isFormField) {
50450             this.form.items.add(fe);
50451         }
50452          
50453         return fe;
50454     }
50455 });
50456
50457 /**
50458  * @class Roo.form.Column
50459  * @extends Roo.form.Layout
50460  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
50461  * @constructor
50462  * @param {Object} config Configuration options
50463  */
50464 Roo.form.Column = function(config){
50465     Roo.form.Column.superclass.constructor.call(this, config);
50466 };
50467
50468 Roo.extend(Roo.form.Column, Roo.form.Layout, {
50469     /**
50470      * @cfg {Number/String} width
50471      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50472      */
50473     /**
50474      * @cfg {String/Object} autoCreate
50475      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
50476      */
50477
50478     // private
50479     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
50480
50481     // private
50482     onRender : function(ct, position){
50483         Roo.form.Column.superclass.onRender.call(this, ct, position);
50484         if(this.width){
50485             this.el.setWidth(this.width);
50486         }
50487     }
50488 });
50489
50490
50491 /**
50492  * @class Roo.form.Row
50493  * @extends Roo.form.Layout
50494  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50495  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
50496  * @constructor
50497  * @param {Object} config Configuration options
50498  */
50499
50500  
50501 Roo.form.Row = function(config){
50502     Roo.form.Row.superclass.constructor.call(this, config);
50503 };
50504  
50505 Roo.extend(Roo.form.Row, Roo.form.Layout, {
50506       /**
50507      * @cfg {Number/String} width
50508      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50509      */
50510     /**
50511      * @cfg {Number/String} height
50512      * The fixed height of the column in pixels or CSS value (defaults to "auto")
50513      */
50514     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
50515     
50516     padWidth : 20,
50517     // private
50518     onRender : function(ct, position){
50519         //console.log('row render');
50520         if(!this.rowTpl){
50521             var t = new Roo.Template(
50522                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
50523                     '<label for="{0}" style="{2}">{1}{4}</label>',
50524                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50525                     '</div>',
50526                 '</div>'
50527             );
50528             t.disableFormats = true;
50529             t.compile();
50530             Roo.form.Layout.prototype.rowTpl = t;
50531         }
50532         this.fieldTpl = this.rowTpl;
50533         
50534         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
50535         var labelWidth = 100;
50536         
50537         if ((this.labelAlign != 'top')) {
50538             if (typeof this.labelWidth == 'number') {
50539                 labelWidth = this.labelWidth
50540             }
50541             this.padWidth =  20 + labelWidth;
50542             
50543         }
50544         
50545         Roo.form.Column.superclass.onRender.call(this, ct, position);
50546         if(this.width){
50547             this.el.setWidth(this.width);
50548         }
50549         if(this.height){
50550             this.el.setHeight(this.height);
50551         }
50552     },
50553     
50554     // private
50555     renderField : function(f){
50556         f.fieldEl = this.fieldTpl.append(this.el, [
50557                f.id, f.fieldLabel,
50558                f.labelStyle||this.labelStyle||'',
50559                this.elementStyle||'',
50560                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
50561                f.itemCls||this.itemCls||'',
50562                f.width ? f.width + this.padWidth : 160 + this.padWidth
50563        ],true);
50564     }
50565 });
50566  
50567
50568 /**
50569  * @class Roo.form.FieldSet
50570  * @extends Roo.form.Layout
50571  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50572  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
50573  * @constructor
50574  * @param {Object} config Configuration options
50575  */
50576 Roo.form.FieldSet = function(config){
50577     Roo.form.FieldSet.superclass.constructor.call(this, config);
50578 };
50579
50580 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
50581     /**
50582      * @cfg {String} legend
50583      * The text to display as the legend for the FieldSet (defaults to '')
50584      */
50585     /**
50586      * @cfg {String/Object} autoCreate
50587      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
50588      */
50589
50590     // private
50591     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
50592
50593     // private
50594     onRender : function(ct, position){
50595         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
50596         if(this.legend){
50597             this.setLegend(this.legend);
50598         }
50599     },
50600
50601     // private
50602     setLegend : function(text){
50603         if(this.rendered){
50604             this.el.child('legend').update(text);
50605         }
50606     }
50607 });/*
50608  * Based on:
50609  * Ext JS Library 1.1.1
50610  * Copyright(c) 2006-2007, Ext JS, LLC.
50611  *
50612  * Originally Released Under LGPL - original licence link has changed is not relivant.
50613  *
50614  * Fork - LGPL
50615  * <script type="text/javascript">
50616  */
50617 /**
50618  * @class Roo.form.VTypes
50619  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
50620  * @static
50621  */
50622 Roo.form.VTypes = function(){
50623     // closure these in so they are only created once.
50624     var alpha = /^[a-zA-Z_]+$/;
50625     var alphanum = /^[a-zA-Z0-9_]+$/;
50626     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
50627     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
50628
50629     // All these messages and functions are configurable
50630     return {
50631         /**
50632          * The function used to validate email addresses
50633          * @param {String} value The email address
50634          */
50635         'email' : function(v){
50636             return email.test(v);
50637         },
50638         /**
50639          * The error text to display when the email validation function returns false
50640          * @type String
50641          */
50642         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
50643         /**
50644          * The keystroke filter mask to be applied on email input
50645          * @type RegExp
50646          */
50647         'emailMask' : /[a-z0-9_\.\-@]/i,
50648
50649         /**
50650          * The function used to validate URLs
50651          * @param {String} value The URL
50652          */
50653         'url' : function(v){
50654             return url.test(v);
50655         },
50656         /**
50657          * The error text to display when the url validation function returns false
50658          * @type String
50659          */
50660         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
50661         
50662         /**
50663          * The function used to validate alpha values
50664          * @param {String} value The value
50665          */
50666         'alpha' : function(v){
50667             return alpha.test(v);
50668         },
50669         /**
50670          * The error text to display when the alpha validation function returns false
50671          * @type String
50672          */
50673         'alphaText' : 'This field should only contain letters and _',
50674         /**
50675          * The keystroke filter mask to be applied on alpha input
50676          * @type RegExp
50677          */
50678         'alphaMask' : /[a-z_]/i,
50679
50680         /**
50681          * The function used to validate alphanumeric values
50682          * @param {String} value The value
50683          */
50684         'alphanum' : function(v){
50685             return alphanum.test(v);
50686         },
50687         /**
50688          * The error text to display when the alphanumeric validation function returns false
50689          * @type String
50690          */
50691         'alphanumText' : 'This field should only contain letters, numbers and _',
50692         /**
50693          * The keystroke filter mask to be applied on alphanumeric input
50694          * @type RegExp
50695          */
50696         'alphanumMask' : /[a-z0-9_]/i
50697     };
50698 }();//<script type="text/javascript">
50699
50700 /**
50701  * @class Roo.form.FCKeditor
50702  * @extends Roo.form.TextArea
50703  * Wrapper around the FCKEditor http://www.fckeditor.net
50704  * @constructor
50705  * Creates a new FCKeditor
50706  * @param {Object} config Configuration options
50707  */
50708 Roo.form.FCKeditor = function(config){
50709     Roo.form.FCKeditor.superclass.constructor.call(this, config);
50710     this.addEvents({
50711          /**
50712          * @event editorinit
50713          * Fired when the editor is initialized - you can add extra handlers here..
50714          * @param {FCKeditor} this
50715          * @param {Object} the FCK object.
50716          */
50717         editorinit : true
50718     });
50719     
50720     
50721 };
50722 Roo.form.FCKeditor.editors = { };
50723 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
50724 {
50725     //defaultAutoCreate : {
50726     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
50727     //},
50728     // private
50729     /**
50730      * @cfg {Object} fck options - see fck manual for details.
50731      */
50732     fckconfig : false,
50733     
50734     /**
50735      * @cfg {Object} fck toolbar set (Basic or Default)
50736      */
50737     toolbarSet : 'Basic',
50738     /**
50739      * @cfg {Object} fck BasePath
50740      */ 
50741     basePath : '/fckeditor/',
50742     
50743     
50744     frame : false,
50745     
50746     value : '',
50747     
50748    
50749     onRender : function(ct, position)
50750     {
50751         if(!this.el){
50752             this.defaultAutoCreate = {
50753                 tag: "textarea",
50754                 style:"width:300px;height:60px;",
50755                 autocomplete: "new-password"
50756             };
50757         }
50758         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
50759         /*
50760         if(this.grow){
50761             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
50762             if(this.preventScrollbars){
50763                 this.el.setStyle("overflow", "hidden");
50764             }
50765             this.el.setHeight(this.growMin);
50766         }
50767         */
50768         //console.log('onrender' + this.getId() );
50769         Roo.form.FCKeditor.editors[this.getId()] = this;
50770          
50771
50772         this.replaceTextarea() ;
50773         
50774     },
50775     
50776     getEditor : function() {
50777         return this.fckEditor;
50778     },
50779     /**
50780      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
50781      * @param {Mixed} value The value to set
50782      */
50783     
50784     
50785     setValue : function(value)
50786     {
50787         //console.log('setValue: ' + value);
50788         
50789         if(typeof(value) == 'undefined') { // not sure why this is happending...
50790             return;
50791         }
50792         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50793         
50794         //if(!this.el || !this.getEditor()) {
50795         //    this.value = value;
50796             //this.setValue.defer(100,this,[value]);    
50797         //    return;
50798         //} 
50799         
50800         if(!this.getEditor()) {
50801             return;
50802         }
50803         
50804         this.getEditor().SetData(value);
50805         
50806         //
50807
50808     },
50809
50810     /**
50811      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
50812      * @return {Mixed} value The field value
50813      */
50814     getValue : function()
50815     {
50816         
50817         if (this.frame && this.frame.dom.style.display == 'none') {
50818             return Roo.form.FCKeditor.superclass.getValue.call(this);
50819         }
50820         
50821         if(!this.el || !this.getEditor()) {
50822            
50823            // this.getValue.defer(100,this); 
50824             return this.value;
50825         }
50826        
50827         
50828         var value=this.getEditor().GetData();
50829         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50830         return Roo.form.FCKeditor.superclass.getValue.call(this);
50831         
50832
50833     },
50834
50835     /**
50836      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
50837      * @return {Mixed} value The field value
50838      */
50839     getRawValue : function()
50840     {
50841         if (this.frame && this.frame.dom.style.display == 'none') {
50842             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50843         }
50844         
50845         if(!this.el || !this.getEditor()) {
50846             //this.getRawValue.defer(100,this); 
50847             return this.value;
50848             return;
50849         }
50850         
50851         
50852         
50853         var value=this.getEditor().GetData();
50854         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
50855         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50856          
50857     },
50858     
50859     setSize : function(w,h) {
50860         
50861         
50862         
50863         //if (this.frame && this.frame.dom.style.display == 'none') {
50864         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50865         //    return;
50866         //}
50867         //if(!this.el || !this.getEditor()) {
50868         //    this.setSize.defer(100,this, [w,h]); 
50869         //    return;
50870         //}
50871         
50872         
50873         
50874         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50875         
50876         this.frame.dom.setAttribute('width', w);
50877         this.frame.dom.setAttribute('height', h);
50878         this.frame.setSize(w,h);
50879         
50880     },
50881     
50882     toggleSourceEdit : function(value) {
50883         
50884       
50885          
50886         this.el.dom.style.display = value ? '' : 'none';
50887         this.frame.dom.style.display = value ?  'none' : '';
50888         
50889     },
50890     
50891     
50892     focus: function(tag)
50893     {
50894         if (this.frame.dom.style.display == 'none') {
50895             return Roo.form.FCKeditor.superclass.focus.call(this);
50896         }
50897         if(!this.el || !this.getEditor()) {
50898             this.focus.defer(100,this, [tag]); 
50899             return;
50900         }
50901         
50902         
50903         
50904         
50905         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
50906         this.getEditor().Focus();
50907         if (tgs.length) {
50908             if (!this.getEditor().Selection.GetSelection()) {
50909                 this.focus.defer(100,this, [tag]); 
50910                 return;
50911             }
50912             
50913             
50914             var r = this.getEditor().EditorDocument.createRange();
50915             r.setStart(tgs[0],0);
50916             r.setEnd(tgs[0],0);
50917             this.getEditor().Selection.GetSelection().removeAllRanges();
50918             this.getEditor().Selection.GetSelection().addRange(r);
50919             this.getEditor().Focus();
50920         }
50921         
50922     },
50923     
50924     
50925     
50926     replaceTextarea : function()
50927     {
50928         if ( document.getElementById( this.getId() + '___Frame' ) ) {
50929             return ;
50930         }
50931         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
50932         //{
50933             // We must check the elements firstly using the Id and then the name.
50934         var oTextarea = document.getElementById( this.getId() );
50935         
50936         var colElementsByName = document.getElementsByName( this.getId() ) ;
50937          
50938         oTextarea.style.display = 'none' ;
50939
50940         if ( oTextarea.tabIndex ) {            
50941             this.TabIndex = oTextarea.tabIndex ;
50942         }
50943         
50944         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
50945         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
50946         this.frame = Roo.get(this.getId() + '___Frame')
50947     },
50948     
50949     _getConfigHtml : function()
50950     {
50951         var sConfig = '' ;
50952
50953         for ( var o in this.fckconfig ) {
50954             sConfig += sConfig.length > 0  ? '&amp;' : '';
50955             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
50956         }
50957
50958         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
50959     },
50960     
50961     
50962     _getIFrameHtml : function()
50963     {
50964         var sFile = 'fckeditor.html' ;
50965         /* no idea what this is about..
50966         try
50967         {
50968             if ( (/fcksource=true/i).test( window.top.location.search ) )
50969                 sFile = 'fckeditor.original.html' ;
50970         }
50971         catch (e) { 
50972         */
50973
50974         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
50975         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
50976         
50977         
50978         var html = '<iframe id="' + this.getId() +
50979             '___Frame" src="' + sLink +
50980             '" width="' + this.width +
50981             '" height="' + this.height + '"' +
50982             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
50983             ' frameborder="0" scrolling="no"></iframe>' ;
50984
50985         return html ;
50986     },
50987     
50988     _insertHtmlBefore : function( html, element )
50989     {
50990         if ( element.insertAdjacentHTML )       {
50991             // IE
50992             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50993         } else { // Gecko
50994             var oRange = document.createRange() ;
50995             oRange.setStartBefore( element ) ;
50996             var oFragment = oRange.createContextualFragment( html );
50997             element.parentNode.insertBefore( oFragment, element ) ;
50998         }
50999     }
51000     
51001     
51002   
51003     
51004     
51005     
51006     
51007
51008 });
51009
51010 //Roo.reg('fckeditor', Roo.form.FCKeditor);
51011
51012 function FCKeditor_OnComplete(editorInstance){
51013     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
51014     f.fckEditor = editorInstance;
51015     //console.log("loaded");
51016     f.fireEvent('editorinit', f, editorInstance);
51017
51018   
51019
51020  
51021
51022
51023
51024
51025
51026
51027
51028
51029
51030
51031
51032
51033
51034
51035
51036 //<script type="text/javascript">
51037 /**
51038  * @class Roo.form.GridField
51039  * @extends Roo.form.Field
51040  * Embed a grid (or editable grid into a form)
51041  * STATUS ALPHA
51042  * 
51043  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
51044  * it needs 
51045  * xgrid.store = Roo.data.Store
51046  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
51047  * xgrid.store.reader = Roo.data.JsonReader 
51048  * 
51049  * 
51050  * @constructor
51051  * Creates a new GridField
51052  * @param {Object} config Configuration options
51053  */
51054 Roo.form.GridField = function(config){
51055     Roo.form.GridField.superclass.constructor.call(this, config);
51056      
51057 };
51058
51059 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
51060     /**
51061      * @cfg {Number} width  - used to restrict width of grid..
51062      */
51063     width : 100,
51064     /**
51065      * @cfg {Number} height - used to restrict height of grid..
51066      */
51067     height : 50,
51068      /**
51069      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
51070          * 
51071          *}
51072      */
51073     xgrid : false, 
51074     /**
51075      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51076      * {tag: "input", type: "checkbox", autocomplete: "off"})
51077      */
51078    // defaultAutoCreate : { tag: 'div' },
51079     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
51080     /**
51081      * @cfg {String} addTitle Text to include for adding a title.
51082      */
51083     addTitle : false,
51084     //
51085     onResize : function(){
51086         Roo.form.Field.superclass.onResize.apply(this, arguments);
51087     },
51088
51089     initEvents : function(){
51090         // Roo.form.Checkbox.superclass.initEvents.call(this);
51091         // has no events...
51092        
51093     },
51094
51095
51096     getResizeEl : function(){
51097         return this.wrap;
51098     },
51099
51100     getPositionEl : function(){
51101         return this.wrap;
51102     },
51103
51104     // private
51105     onRender : function(ct, position){
51106         
51107         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
51108         var style = this.style;
51109         delete this.style;
51110         
51111         Roo.form.GridField.superclass.onRender.call(this, ct, position);
51112         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
51113         this.viewEl = this.wrap.createChild({ tag: 'div' });
51114         if (style) {
51115             this.viewEl.applyStyles(style);
51116         }
51117         if (this.width) {
51118             this.viewEl.setWidth(this.width);
51119         }
51120         if (this.height) {
51121             this.viewEl.setHeight(this.height);
51122         }
51123         //if(this.inputValue !== undefined){
51124         //this.setValue(this.value);
51125         
51126         
51127         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
51128         
51129         
51130         this.grid.render();
51131         this.grid.getDataSource().on('remove', this.refreshValue, this);
51132         this.grid.getDataSource().on('update', this.refreshValue, this);
51133         this.grid.on('afteredit', this.refreshValue, this);
51134  
51135     },
51136      
51137     
51138     /**
51139      * Sets the value of the item. 
51140      * @param {String} either an object  or a string..
51141      */
51142     setValue : function(v){
51143         //this.value = v;
51144         v = v || []; // empty set..
51145         // this does not seem smart - it really only affects memoryproxy grids..
51146         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
51147             var ds = this.grid.getDataSource();
51148             // assumes a json reader..
51149             var data = {}
51150             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
51151             ds.loadData( data);
51152         }
51153         // clear selection so it does not get stale.
51154         if (this.grid.sm) { 
51155             this.grid.sm.clearSelections();
51156         }
51157         
51158         Roo.form.GridField.superclass.setValue.call(this, v);
51159         this.refreshValue();
51160         // should load data in the grid really....
51161     },
51162     
51163     // private
51164     refreshValue: function() {
51165          var val = [];
51166         this.grid.getDataSource().each(function(r) {
51167             val.push(r.data);
51168         });
51169         this.el.dom.value = Roo.encode(val);
51170     }
51171     
51172      
51173     
51174     
51175 });/*
51176  * Based on:
51177  * Ext JS Library 1.1.1
51178  * Copyright(c) 2006-2007, Ext JS, LLC.
51179  *
51180  * Originally Released Under LGPL - original licence link has changed is not relivant.
51181  *
51182  * Fork - LGPL
51183  * <script type="text/javascript">
51184  */
51185 /**
51186  * @class Roo.form.DisplayField
51187  * @extends Roo.form.Field
51188  * A generic Field to display non-editable data.
51189  * @cfg {Boolean} closable (true|false) default false
51190  * @constructor
51191  * Creates a new Display Field item.
51192  * @param {Object} config Configuration options
51193  */
51194 Roo.form.DisplayField = function(config){
51195     Roo.form.DisplayField.superclass.constructor.call(this, config);
51196     
51197     this.addEvents({
51198         /**
51199          * @event close
51200          * Fires after the click the close btn
51201              * @param {Roo.form.DisplayField} this
51202              */
51203         close : true
51204     });
51205 };
51206
51207 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
51208     inputType:      'hidden',
51209     allowBlank:     true,
51210     readOnly:         true,
51211     
51212  
51213     /**
51214      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51215      */
51216     focusClass : undefined,
51217     /**
51218      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51219      */
51220     fieldClass: 'x-form-field',
51221     
51222      /**
51223      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
51224      */
51225     valueRenderer: undefined,
51226     
51227     width: 100,
51228     /**
51229      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51230      * {tag: "input", type: "checkbox", autocomplete: "off"})
51231      */
51232      
51233  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
51234  
51235     closable : false,
51236     
51237     onResize : function(){
51238         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
51239         
51240     },
51241
51242     initEvents : function(){
51243         // Roo.form.Checkbox.superclass.initEvents.call(this);
51244         // has no events...
51245         
51246         if(this.closable){
51247             this.closeEl.on('click', this.onClose, this);
51248         }
51249        
51250     },
51251
51252
51253     getResizeEl : function(){
51254         return this.wrap;
51255     },
51256
51257     getPositionEl : function(){
51258         return this.wrap;
51259     },
51260
51261     // private
51262     onRender : function(ct, position){
51263         
51264         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
51265         //if(this.inputValue !== undefined){
51266         this.wrap = this.el.wrap();
51267         
51268         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
51269         
51270         if(this.closable){
51271             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51272         }
51273         
51274         if (this.bodyStyle) {
51275             this.viewEl.applyStyles(this.bodyStyle);
51276         }
51277         //this.viewEl.setStyle('padding', '2px');
51278         
51279         this.setValue(this.value);
51280         
51281     },
51282 /*
51283     // private
51284     initValue : Roo.emptyFn,
51285
51286   */
51287
51288         // private
51289     onClick : function(){
51290         
51291     },
51292
51293     /**
51294      * Sets the checked state of the checkbox.
51295      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51296      */
51297     setValue : function(v){
51298         this.value = v;
51299         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51300         // this might be called before we have a dom element..
51301         if (!this.viewEl) {
51302             return;
51303         }
51304         this.viewEl.dom.innerHTML = html;
51305         Roo.form.DisplayField.superclass.setValue.call(this, v);
51306
51307     },
51308     
51309     onClose : function(e)
51310     {
51311         e.preventDefault();
51312         
51313         this.fireEvent('close', this);
51314     }
51315 });/*
51316  * 
51317  * Licence- LGPL
51318  * 
51319  */
51320
51321 /**
51322  * @class Roo.form.DayPicker
51323  * @extends Roo.form.Field
51324  * A Day picker show [M] [T] [W] ....
51325  * @constructor
51326  * Creates a new Day Picker
51327  * @param {Object} config Configuration options
51328  */
51329 Roo.form.DayPicker= function(config){
51330     Roo.form.DayPicker.superclass.constructor.call(this, config);
51331      
51332 };
51333
51334 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51335     /**
51336      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51337      */
51338     focusClass : undefined,
51339     /**
51340      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51341      */
51342     fieldClass: "x-form-field",
51343    
51344     /**
51345      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51346      * {tag: "input", type: "checkbox", autocomplete: "off"})
51347      */
51348     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
51349     
51350    
51351     actionMode : 'viewEl', 
51352     //
51353     // private
51354  
51355     inputType : 'hidden',
51356     
51357      
51358     inputElement: false, // real input element?
51359     basedOn: false, // ????
51360     
51361     isFormField: true, // not sure where this is needed!!!!
51362
51363     onResize : function(){
51364         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
51365         if(!this.boxLabel){
51366             this.el.alignTo(this.wrap, 'c-c');
51367         }
51368     },
51369
51370     initEvents : function(){
51371         Roo.form.Checkbox.superclass.initEvents.call(this);
51372         this.el.on("click", this.onClick,  this);
51373         this.el.on("change", this.onClick,  this);
51374     },
51375
51376
51377     getResizeEl : function(){
51378         return this.wrap;
51379     },
51380
51381     getPositionEl : function(){
51382         return this.wrap;
51383     },
51384
51385     
51386     // private
51387     onRender : function(ct, position){
51388         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
51389        
51390         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
51391         
51392         var r1 = '<table><tr>';
51393         var r2 = '<tr class="x-form-daypick-icons">';
51394         for (var i=0; i < 7; i++) {
51395             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
51396             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
51397         }
51398         
51399         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
51400         viewEl.select('img').on('click', this.onClick, this);
51401         this.viewEl = viewEl;   
51402         
51403         
51404         // this will not work on Chrome!!!
51405         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
51406         this.el.on('propertychange', this.setFromHidden,  this);  //ie
51407         
51408         
51409           
51410
51411     },
51412
51413     // private
51414     initValue : Roo.emptyFn,
51415
51416     /**
51417      * Returns the checked state of the checkbox.
51418      * @return {Boolean} True if checked, else false
51419      */
51420     getValue : function(){
51421         return this.el.dom.value;
51422         
51423     },
51424
51425         // private
51426     onClick : function(e){ 
51427         //this.setChecked(!this.checked);
51428         Roo.get(e.target).toggleClass('x-menu-item-checked');
51429         this.refreshValue();
51430         //if(this.el.dom.checked != this.checked){
51431         //    this.setValue(this.el.dom.checked);
51432        // }
51433     },
51434     
51435     // private
51436     refreshValue : function()
51437     {
51438         var val = '';
51439         this.viewEl.select('img',true).each(function(e,i,n)  {
51440             val += e.is(".x-menu-item-checked") ? String(n) : '';
51441         });
51442         this.setValue(val, true);
51443     },
51444
51445     /**
51446      * Sets the checked state of the checkbox.
51447      * On is always based on a string comparison between inputValue and the param.
51448      * @param {Boolean/String} value - the value to set 
51449      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
51450      */
51451     setValue : function(v,suppressEvent){
51452         if (!this.el.dom) {
51453             return;
51454         }
51455         var old = this.el.dom.value ;
51456         this.el.dom.value = v;
51457         if (suppressEvent) {
51458             return ;
51459         }
51460          
51461         // update display..
51462         this.viewEl.select('img',true).each(function(e,i,n)  {
51463             
51464             var on = e.is(".x-menu-item-checked");
51465             var newv = v.indexOf(String(n)) > -1;
51466             if (on != newv) {
51467                 e.toggleClass('x-menu-item-checked');
51468             }
51469             
51470         });
51471         
51472         
51473         this.fireEvent('change', this, v, old);
51474         
51475         
51476     },
51477    
51478     // handle setting of hidden value by some other method!!?!?
51479     setFromHidden: function()
51480     {
51481         if(!this.el){
51482             return;
51483         }
51484         //console.log("SET FROM HIDDEN");
51485         //alert('setFrom hidden');
51486         this.setValue(this.el.dom.value);
51487     },
51488     
51489     onDestroy : function()
51490     {
51491         if(this.viewEl){
51492             Roo.get(this.viewEl).remove();
51493         }
51494          
51495         Roo.form.DayPicker.superclass.onDestroy.call(this);
51496     }
51497
51498 });/*
51499  * RooJS Library 1.1.1
51500  * Copyright(c) 2008-2011  Alan Knowles
51501  *
51502  * License - LGPL
51503  */
51504  
51505
51506 /**
51507  * @class Roo.form.ComboCheck
51508  * @extends Roo.form.ComboBox
51509  * A combobox for multiple select items.
51510  *
51511  * FIXME - could do with a reset button..
51512  * 
51513  * @constructor
51514  * Create a new ComboCheck
51515  * @param {Object} config Configuration options
51516  */
51517 Roo.form.ComboCheck = function(config){
51518     Roo.form.ComboCheck.superclass.constructor.call(this, config);
51519     // should verify some data...
51520     // like
51521     // hiddenName = required..
51522     // displayField = required
51523     // valudField == required
51524     var req= [ 'hiddenName', 'displayField', 'valueField' ];
51525     var _t = this;
51526     Roo.each(req, function(e) {
51527         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
51528             throw "Roo.form.ComboCheck : missing value for: " + e;
51529         }
51530     });
51531     
51532     
51533 };
51534
51535 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
51536      
51537      
51538     editable : false,
51539      
51540     selectedClass: 'x-menu-item-checked', 
51541     
51542     // private
51543     onRender : function(ct, position){
51544         var _t = this;
51545         
51546         
51547         
51548         if(!this.tpl){
51549             var cls = 'x-combo-list';
51550
51551             
51552             this.tpl =  new Roo.Template({
51553                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
51554                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
51555                    '<span>{' + this.displayField + '}</span>' +
51556                     '</div>' 
51557                 
51558             });
51559         }
51560  
51561         
51562         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
51563         this.view.singleSelect = false;
51564         this.view.multiSelect = true;
51565         this.view.toggleSelect = true;
51566         this.pageTb.add(new Roo.Toolbar.Fill(), {
51567             
51568             text: 'Done',
51569             handler: function()
51570             {
51571                 _t.collapse();
51572             }
51573         });
51574     },
51575     
51576     onViewOver : function(e, t){
51577         // do nothing...
51578         return;
51579         
51580     },
51581     
51582     onViewClick : function(doFocus,index){
51583         return;
51584         
51585     },
51586     select: function () {
51587         //Roo.log("SELECT CALLED");
51588     },
51589      
51590     selectByValue : function(xv, scrollIntoView){
51591         var ar = this.getValueArray();
51592         var sels = [];
51593         
51594         Roo.each(ar, function(v) {
51595             if(v === undefined || v === null){
51596                 return;
51597             }
51598             var r = this.findRecord(this.valueField, v);
51599             if(r){
51600                 sels.push(this.store.indexOf(r))
51601                 
51602             }
51603         },this);
51604         this.view.select(sels);
51605         return false;
51606     },
51607     
51608     
51609     
51610     onSelect : function(record, index){
51611        // Roo.log("onselect Called");
51612        // this is only called by the clear button now..
51613         this.view.clearSelections();
51614         this.setValue('[]');
51615         if (this.value != this.valueBefore) {
51616             this.fireEvent('change', this, this.value, this.valueBefore);
51617             this.valueBefore = this.value;
51618         }
51619     },
51620     getValueArray : function()
51621     {
51622         var ar = [] ;
51623         
51624         try {
51625             //Roo.log(this.value);
51626             if (typeof(this.value) == 'undefined') {
51627                 return [];
51628             }
51629             var ar = Roo.decode(this.value);
51630             return  ar instanceof Array ? ar : []; //?? valid?
51631             
51632         } catch(e) {
51633             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
51634             return [];
51635         }
51636          
51637     },
51638     expand : function ()
51639     {
51640         
51641         Roo.form.ComboCheck.superclass.expand.call(this);
51642         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
51643         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
51644         
51645
51646     },
51647     
51648     collapse : function(){
51649         Roo.form.ComboCheck.superclass.collapse.call(this);
51650         var sl = this.view.getSelectedIndexes();
51651         var st = this.store;
51652         var nv = [];
51653         var tv = [];
51654         var r;
51655         Roo.each(sl, function(i) {
51656             r = st.getAt(i);
51657             nv.push(r.get(this.valueField));
51658         },this);
51659         this.setValue(Roo.encode(nv));
51660         if (this.value != this.valueBefore) {
51661
51662             this.fireEvent('change', this, this.value, this.valueBefore);
51663             this.valueBefore = this.value;
51664         }
51665         
51666     },
51667     
51668     setValue : function(v){
51669         // Roo.log(v);
51670         this.value = v;
51671         
51672         var vals = this.getValueArray();
51673         var tv = [];
51674         Roo.each(vals, function(k) {
51675             var r = this.findRecord(this.valueField, k);
51676             if(r){
51677                 tv.push(r.data[this.displayField]);
51678             }else if(this.valueNotFoundText !== undefined){
51679                 tv.push( this.valueNotFoundText );
51680             }
51681         },this);
51682        // Roo.log(tv);
51683         
51684         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
51685         this.hiddenField.value = v;
51686         this.value = v;
51687     }
51688     
51689 });/*
51690  * Based on:
51691  * Ext JS Library 1.1.1
51692  * Copyright(c) 2006-2007, Ext JS, LLC.
51693  *
51694  * Originally Released Under LGPL - original licence link has changed is not relivant.
51695  *
51696  * Fork - LGPL
51697  * <script type="text/javascript">
51698  */
51699  
51700 /**
51701  * @class Roo.form.Signature
51702  * @extends Roo.form.Field
51703  * Signature field.  
51704  * @constructor
51705  * 
51706  * @param {Object} config Configuration options
51707  */
51708
51709 Roo.form.Signature = function(config){
51710     Roo.form.Signature.superclass.constructor.call(this, config);
51711     
51712     this.addEvents({// not in used??
51713          /**
51714          * @event confirm
51715          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
51716              * @param {Roo.form.Signature} combo This combo box
51717              */
51718         'confirm' : true,
51719         /**
51720          * @event reset
51721          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
51722              * @param {Roo.form.ComboBox} combo This combo box
51723              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
51724              */
51725         'reset' : true
51726     });
51727 };
51728
51729 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
51730     /**
51731      * @cfg {Object} labels Label to use when rendering a form.
51732      * defaults to 
51733      * labels : { 
51734      *      clear : "Clear",
51735      *      confirm : "Confirm"
51736      *  }
51737      */
51738     labels : { 
51739         clear : "Clear",
51740         confirm : "Confirm"
51741     },
51742     /**
51743      * @cfg {Number} width The signature panel width (defaults to 300)
51744      */
51745     width: 300,
51746     /**
51747      * @cfg {Number} height The signature panel height (defaults to 100)
51748      */
51749     height : 100,
51750     /**
51751      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
51752      */
51753     allowBlank : false,
51754     
51755     //private
51756     // {Object} signPanel The signature SVG panel element (defaults to {})
51757     signPanel : {},
51758     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
51759     isMouseDown : false,
51760     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
51761     isConfirmed : false,
51762     // {String} signatureTmp SVG mapping string (defaults to empty string)
51763     signatureTmp : '',
51764     
51765     
51766     defaultAutoCreate : { // modified by initCompnoent..
51767         tag: "input",
51768         type:"hidden"
51769     },
51770
51771     // private
51772     onRender : function(ct, position){
51773         
51774         Roo.form.Signature.superclass.onRender.call(this, ct, position);
51775         
51776         this.wrap = this.el.wrap({
51777             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
51778         });
51779         
51780         this.createToolbar(this);
51781         this.signPanel = this.wrap.createChild({
51782                 tag: 'div',
51783                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
51784             }, this.el
51785         );
51786             
51787         this.svgID = Roo.id();
51788         this.svgEl = this.signPanel.createChild({
51789               xmlns : 'http://www.w3.org/2000/svg',
51790               tag : 'svg',
51791               id : this.svgID + "-svg",
51792               width: this.width,
51793               height: this.height,
51794               viewBox: '0 0 '+this.width+' '+this.height,
51795               cn : [
51796                 {
51797                     tag: "rect",
51798                     id: this.svgID + "-svg-r",
51799                     width: this.width,
51800                     height: this.height,
51801                     fill: "#ffa"
51802                 },
51803                 {
51804                     tag: "line",
51805                     id: this.svgID + "-svg-l",
51806                     x1: "0", // start
51807                     y1: (this.height*0.8), // start set the line in 80% of height
51808                     x2: this.width, // end
51809                     y2: (this.height*0.8), // end set the line in 80% of height
51810                     'stroke': "#666",
51811                     'stroke-width': "1",
51812                     'stroke-dasharray': "3",
51813                     'shape-rendering': "crispEdges",
51814                     'pointer-events': "none"
51815                 },
51816                 {
51817                     tag: "path",
51818                     id: this.svgID + "-svg-p",
51819                     'stroke': "navy",
51820                     'stroke-width': "3",
51821                     'fill': "none",
51822                     'pointer-events': 'none'
51823                 }
51824               ]
51825         });
51826         this.createSVG();
51827         this.svgBox = this.svgEl.dom.getScreenCTM();
51828     },
51829     createSVG : function(){ 
51830         var svg = this.signPanel;
51831         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
51832         var t = this;
51833
51834         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
51835         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
51836         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
51837         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
51838         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
51839         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
51840         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
51841         
51842     },
51843     isTouchEvent : function(e){
51844         return e.type.match(/^touch/);
51845     },
51846     getCoords : function (e) {
51847         var pt    = this.svgEl.dom.createSVGPoint();
51848         pt.x = e.clientX; 
51849         pt.y = e.clientY;
51850         if (this.isTouchEvent(e)) {
51851             pt.x =  e.targetTouches[0].clientX;
51852             pt.y = e.targetTouches[0].clientY;
51853         }
51854         var a = this.svgEl.dom.getScreenCTM();
51855         var b = a.inverse();
51856         var mx = pt.matrixTransform(b);
51857         return mx.x + ',' + mx.y;
51858     },
51859     //mouse event headler 
51860     down : function (e) {
51861         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
51862         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
51863         
51864         this.isMouseDown = true;
51865         
51866         e.preventDefault();
51867     },
51868     move : function (e) {
51869         if (this.isMouseDown) {
51870             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
51871             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
51872         }
51873         
51874         e.preventDefault();
51875     },
51876     up : function (e) {
51877         this.isMouseDown = false;
51878         var sp = this.signatureTmp.split(' ');
51879         
51880         if(sp.length > 1){
51881             if(!sp[sp.length-2].match(/^L/)){
51882                 sp.pop();
51883                 sp.pop();
51884                 sp.push("");
51885                 this.signatureTmp = sp.join(" ");
51886             }
51887         }
51888         if(this.getValue() != this.signatureTmp){
51889             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51890             this.isConfirmed = false;
51891         }
51892         e.preventDefault();
51893     },
51894     
51895     /**
51896      * Protected method that will not generally be called directly. It
51897      * is called when the editor creates its toolbar. Override this method if you need to
51898      * add custom toolbar buttons.
51899      * @param {HtmlEditor} editor
51900      */
51901     createToolbar : function(editor){
51902          function btn(id, toggle, handler){
51903             var xid = fid + '-'+ id ;
51904             return {
51905                 id : xid,
51906                 cmd : id,
51907                 cls : 'x-btn-icon x-edit-'+id,
51908                 enableToggle:toggle !== false,
51909                 scope: editor, // was editor...
51910                 handler:handler||editor.relayBtnCmd,
51911                 clickEvent:'mousedown',
51912                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
51913                 tabIndex:-1
51914             };
51915         }
51916         
51917         
51918         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
51919         this.tb = tb;
51920         this.tb.add(
51921            {
51922                 cls : ' x-signature-btn x-signature-'+id,
51923                 scope: editor, // was editor...
51924                 handler: this.reset,
51925                 clickEvent:'mousedown',
51926                 text: this.labels.clear
51927             },
51928             {
51929                  xtype : 'Fill',
51930                  xns: Roo.Toolbar
51931             }, 
51932             {
51933                 cls : '  x-signature-btn x-signature-'+id,
51934                 scope: editor, // was editor...
51935                 handler: this.confirmHandler,
51936                 clickEvent:'mousedown',
51937                 text: this.labels.confirm
51938             }
51939         );
51940     
51941     },
51942     //public
51943     /**
51944      * when user is clicked confirm then show this image.....
51945      * 
51946      * @return {String} Image Data URI
51947      */
51948     getImageDataURI : function(){
51949         var svg = this.svgEl.dom.parentNode.innerHTML;
51950         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
51951         return src; 
51952     },
51953     /**
51954      * 
51955      * @return {Boolean} this.isConfirmed
51956      */
51957     getConfirmed : function(){
51958         return this.isConfirmed;
51959     },
51960     /**
51961      * 
51962      * @return {Number} this.width
51963      */
51964     getWidth : function(){
51965         return this.width;
51966     },
51967     /**
51968      * 
51969      * @return {Number} this.height
51970      */
51971     getHeight : function(){
51972         return this.height;
51973     },
51974     // private
51975     getSignature : function(){
51976         return this.signatureTmp;
51977     },
51978     // private
51979     reset : function(){
51980         this.signatureTmp = '';
51981         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51982         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
51983         this.isConfirmed = false;
51984         Roo.form.Signature.superclass.reset.call(this);
51985     },
51986     setSignature : function(s){
51987         this.signatureTmp = s;
51988         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51989         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51990         this.setValue(s);
51991         this.isConfirmed = false;
51992         Roo.form.Signature.superclass.reset.call(this);
51993     }, 
51994     test : function(){
51995 //        Roo.log(this.signPanel.dom.contentWindow.up())
51996     },
51997     //private
51998     setConfirmed : function(){
51999         
52000         
52001         
52002 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
52003     },
52004     // private
52005     confirmHandler : function(){
52006         if(!this.getSignature()){
52007             return;
52008         }
52009         
52010         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
52011         this.setValue(this.getSignature());
52012         this.isConfirmed = true;
52013         
52014         this.fireEvent('confirm', this);
52015     },
52016     // private
52017     // Subclasses should provide the validation implementation by overriding this
52018     validateValue : function(value){
52019         if(this.allowBlank){
52020             return true;
52021         }
52022         
52023         if(this.isConfirmed){
52024             return true;
52025         }
52026         return false;
52027     }
52028 });/*
52029  * Based on:
52030  * Ext JS Library 1.1.1
52031  * Copyright(c) 2006-2007, Ext JS, LLC.
52032  *
52033  * Originally Released Under LGPL - original licence link has changed is not relivant.
52034  *
52035  * Fork - LGPL
52036  * <script type="text/javascript">
52037  */
52038  
52039
52040 /**
52041  * @class Roo.form.ComboBox
52042  * @extends Roo.form.TriggerField
52043  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
52044  * @constructor
52045  * Create a new ComboBox.
52046  * @param {Object} config Configuration options
52047  */
52048 Roo.form.Select = function(config){
52049     Roo.form.Select.superclass.constructor.call(this, config);
52050      
52051 };
52052
52053 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
52054     /**
52055      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
52056      */
52057     /**
52058      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
52059      * rendering into an Roo.Editor, defaults to false)
52060      */
52061     /**
52062      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
52063      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
52064      */
52065     /**
52066      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
52067      */
52068     /**
52069      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
52070      * the dropdown list (defaults to undefined, with no header element)
52071      */
52072
52073      /**
52074      * @cfg {String/Roo.Template} tpl The template to use to render the output
52075      */
52076      
52077     // private
52078     defaultAutoCreate : {tag: "select"  },
52079     /**
52080      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
52081      */
52082     listWidth: undefined,
52083     /**
52084      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
52085      * mode = 'remote' or 'text' if mode = 'local')
52086      */
52087     displayField: undefined,
52088     /**
52089      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
52090      * mode = 'remote' or 'value' if mode = 'local'). 
52091      * Note: use of a valueField requires the user make a selection
52092      * in order for a value to be mapped.
52093      */
52094     valueField: undefined,
52095     
52096     
52097     /**
52098      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
52099      * field's data value (defaults to the underlying DOM element's name)
52100      */
52101     hiddenName: undefined,
52102     /**
52103      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
52104      */
52105     listClass: '',
52106     /**
52107      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
52108      */
52109     selectedClass: 'x-combo-selected',
52110     /**
52111      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
52112      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
52113      * which displays a downward arrow icon).
52114      */
52115     triggerClass : 'x-form-arrow-trigger',
52116     /**
52117      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
52118      */
52119     shadow:'sides',
52120     /**
52121      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
52122      * anchor positions (defaults to 'tl-bl')
52123      */
52124     listAlign: 'tl-bl?',
52125     /**
52126      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
52127      */
52128     maxHeight: 300,
52129     /**
52130      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
52131      * query specified by the allQuery config option (defaults to 'query')
52132      */
52133     triggerAction: 'query',
52134     /**
52135      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
52136      * (defaults to 4, does not apply if editable = false)
52137      */
52138     minChars : 4,
52139     /**
52140      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
52141      * delay (typeAheadDelay) if it matches a known value (defaults to false)
52142      */
52143     typeAhead: false,
52144     /**
52145      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
52146      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
52147      */
52148     queryDelay: 500,
52149     /**
52150      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
52151      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
52152      */
52153     pageSize: 0,
52154     /**
52155      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
52156      * when editable = true (defaults to false)
52157      */
52158     selectOnFocus:false,
52159     /**
52160      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
52161      */
52162     queryParam: 'query',
52163     /**
52164      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
52165      * when mode = 'remote' (defaults to 'Loading...')
52166      */
52167     loadingText: 'Loading...',
52168     /**
52169      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
52170      */
52171     resizable: false,
52172     /**
52173      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
52174      */
52175     handleHeight : 8,
52176     /**
52177      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
52178      * traditional select (defaults to true)
52179      */
52180     editable: true,
52181     /**
52182      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
52183      */
52184     allQuery: '',
52185     /**
52186      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
52187      */
52188     mode: 'remote',
52189     /**
52190      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
52191      * listWidth has a higher value)
52192      */
52193     minListWidth : 70,
52194     /**
52195      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
52196      * allow the user to set arbitrary text into the field (defaults to false)
52197      */
52198     forceSelection:false,
52199     /**
52200      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
52201      * if typeAhead = true (defaults to 250)
52202      */
52203     typeAheadDelay : 250,
52204     /**
52205      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
52206      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
52207      */
52208     valueNotFoundText : undefined,
52209     
52210     /**
52211      * @cfg {String} defaultValue The value displayed after loading the store.
52212      */
52213     defaultValue: '',
52214     
52215     /**
52216      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
52217      */
52218     blockFocus : false,
52219     
52220     /**
52221      * @cfg {Boolean} disableClear Disable showing of clear button.
52222      */
52223     disableClear : false,
52224     /**
52225      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
52226      */
52227     alwaysQuery : false,
52228     
52229     //private
52230     addicon : false,
52231     editicon: false,
52232     
52233     // element that contains real text value.. (when hidden is used..)
52234      
52235     // private
52236     onRender : function(ct, position){
52237         Roo.form.Field.prototype.onRender.call(this, ct, position);
52238         
52239         if(this.store){
52240             this.store.on('beforeload', this.onBeforeLoad, this);
52241             this.store.on('load', this.onLoad, this);
52242             this.store.on('loadexception', this.onLoadException, this);
52243             this.store.load({});
52244         }
52245         
52246         
52247         
52248     },
52249
52250     // private
52251     initEvents : function(){
52252         //Roo.form.ComboBox.superclass.initEvents.call(this);
52253  
52254     },
52255
52256     onDestroy : function(){
52257        
52258         if(this.store){
52259             this.store.un('beforeload', this.onBeforeLoad, this);
52260             this.store.un('load', this.onLoad, this);
52261             this.store.un('loadexception', this.onLoadException, this);
52262         }
52263         //Roo.form.ComboBox.superclass.onDestroy.call(this);
52264     },
52265
52266     // private
52267     fireKey : function(e){
52268         if(e.isNavKeyPress() && !this.list.isVisible()){
52269             this.fireEvent("specialkey", this, e);
52270         }
52271     },
52272
52273     // private
52274     onResize: function(w, h){
52275         
52276         return; 
52277     
52278         
52279     },
52280
52281     /**
52282      * Allow or prevent the user from directly editing the field text.  If false is passed,
52283      * the user will only be able to select from the items defined in the dropdown list.  This method
52284      * is the runtime equivalent of setting the 'editable' config option at config time.
52285      * @param {Boolean} value True to allow the user to directly edit the field text
52286      */
52287     setEditable : function(value){
52288          
52289     },
52290
52291     // private
52292     onBeforeLoad : function(){
52293         
52294         Roo.log("Select before load");
52295         return;
52296     
52297         this.innerList.update(this.loadingText ?
52298                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52299         //this.restrictHeight();
52300         this.selectedIndex = -1;
52301     },
52302
52303     // private
52304     onLoad : function(){
52305
52306     
52307         var dom = this.el.dom;
52308         dom.innerHTML = '';
52309          var od = dom.ownerDocument;
52310          
52311         if (this.emptyText) {
52312             var op = od.createElement('option');
52313             op.setAttribute('value', '');
52314             op.innerHTML = String.format('{0}', this.emptyText);
52315             dom.appendChild(op);
52316         }
52317         if(this.store.getCount() > 0){
52318            
52319             var vf = this.valueField;
52320             var df = this.displayField;
52321             this.store.data.each(function(r) {
52322                 // which colmsn to use... testing - cdoe / title..
52323                 var op = od.createElement('option');
52324                 op.setAttribute('value', r.data[vf]);
52325                 op.innerHTML = String.format('{0}', r.data[df]);
52326                 dom.appendChild(op);
52327             });
52328             if (typeof(this.defaultValue != 'undefined')) {
52329                 this.setValue(this.defaultValue);
52330             }
52331             
52332              
52333         }else{
52334             //this.onEmptyResults();
52335         }
52336         //this.el.focus();
52337     },
52338     // private
52339     onLoadException : function()
52340     {
52341         dom.innerHTML = '';
52342             
52343         Roo.log("Select on load exception");
52344         return;
52345     
52346         this.collapse();
52347         Roo.log(this.store.reader.jsonData);
52348         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52349             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52350         }
52351         
52352         
52353     },
52354     // private
52355     onTypeAhead : function(){
52356          
52357     },
52358
52359     // private
52360     onSelect : function(record, index){
52361         Roo.log('on select?');
52362         return;
52363         if(this.fireEvent('beforeselect', this, record, index) !== false){
52364             this.setFromData(index > -1 ? record.data : false);
52365             this.collapse();
52366             this.fireEvent('select', this, record, index);
52367         }
52368     },
52369
52370     /**
52371      * Returns the currently selected field value or empty string if no value is set.
52372      * @return {String} value The selected value
52373      */
52374     getValue : function(){
52375         var dom = this.el.dom;
52376         this.value = dom.options[dom.selectedIndex].value;
52377         return this.value;
52378         
52379     },
52380
52381     /**
52382      * Clears any text/value currently set in the field
52383      */
52384     clearValue : function(){
52385         this.value = '';
52386         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
52387         
52388     },
52389
52390     /**
52391      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
52392      * will be displayed in the field.  If the value does not match the data value of an existing item,
52393      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
52394      * Otherwise the field will be blank (although the value will still be set).
52395      * @param {String} value The value to match
52396      */
52397     setValue : function(v){
52398         var d = this.el.dom;
52399         for (var i =0; i < d.options.length;i++) {
52400             if (v == d.options[i].value) {
52401                 d.selectedIndex = i;
52402                 this.value = v;
52403                 return;
52404             }
52405         }
52406         this.clearValue();
52407     },
52408     /**
52409      * @property {Object} the last set data for the element
52410      */
52411     
52412     lastData : false,
52413     /**
52414      * Sets the value of the field based on a object which is related to the record format for the store.
52415      * @param {Object} value the value to set as. or false on reset?
52416      */
52417     setFromData : function(o){
52418         Roo.log('setfrom data?');
52419          
52420         
52421         
52422     },
52423     // private
52424     reset : function(){
52425         this.clearValue();
52426     },
52427     // private
52428     findRecord : function(prop, value){
52429         
52430         return false;
52431     
52432         var record;
52433         if(this.store.getCount() > 0){
52434             this.store.each(function(r){
52435                 if(r.data[prop] == value){
52436                     record = r;
52437                     return false;
52438                 }
52439                 return true;
52440             });
52441         }
52442         return record;
52443     },
52444     
52445     getName: function()
52446     {
52447         // returns hidden if it's set..
52448         if (!this.rendered) {return ''};
52449         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
52450         
52451     },
52452      
52453
52454     
52455
52456     // private
52457     onEmptyResults : function(){
52458         Roo.log('empty results');
52459         //this.collapse();
52460     },
52461
52462     /**
52463      * Returns true if the dropdown list is expanded, else false.
52464      */
52465     isExpanded : function(){
52466         return false;
52467     },
52468
52469     /**
52470      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
52471      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52472      * @param {String} value The data value of the item to select
52473      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52474      * selected item if it is not currently in view (defaults to true)
52475      * @return {Boolean} True if the value matched an item in the list, else false
52476      */
52477     selectByValue : function(v, scrollIntoView){
52478         Roo.log('select By Value');
52479         return false;
52480     
52481         if(v !== undefined && v !== null){
52482             var r = this.findRecord(this.valueField || this.displayField, v);
52483             if(r){
52484                 this.select(this.store.indexOf(r), scrollIntoView);
52485                 return true;
52486             }
52487         }
52488         return false;
52489     },
52490
52491     /**
52492      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
52493      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52494      * @param {Number} index The zero-based index of the list item to select
52495      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52496      * selected item if it is not currently in view (defaults to true)
52497      */
52498     select : function(index, scrollIntoView){
52499         Roo.log('select ');
52500         return  ;
52501         
52502         this.selectedIndex = index;
52503         this.view.select(index);
52504         if(scrollIntoView !== false){
52505             var el = this.view.getNode(index);
52506             if(el){
52507                 this.innerList.scrollChildIntoView(el, false);
52508             }
52509         }
52510     },
52511
52512       
52513
52514     // private
52515     validateBlur : function(){
52516         
52517         return;
52518         
52519     },
52520
52521     // private
52522     initQuery : function(){
52523         this.doQuery(this.getRawValue());
52524     },
52525
52526     // private
52527     doForce : function(){
52528         if(this.el.dom.value.length > 0){
52529             this.el.dom.value =
52530                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
52531              
52532         }
52533     },
52534
52535     /**
52536      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
52537      * query allowing the query action to be canceled if needed.
52538      * @param {String} query The SQL query to execute
52539      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
52540      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
52541      * saved in the current store (defaults to false)
52542      */
52543     doQuery : function(q, forceAll){
52544         
52545         Roo.log('doQuery?');
52546         if(q === undefined || q === null){
52547             q = '';
52548         }
52549         var qe = {
52550             query: q,
52551             forceAll: forceAll,
52552             combo: this,
52553             cancel:false
52554         };
52555         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
52556             return false;
52557         }
52558         q = qe.query;
52559         forceAll = qe.forceAll;
52560         if(forceAll === true || (q.length >= this.minChars)){
52561             if(this.lastQuery != q || this.alwaysQuery){
52562                 this.lastQuery = q;
52563                 if(this.mode == 'local'){
52564                     this.selectedIndex = -1;
52565                     if(forceAll){
52566                         this.store.clearFilter();
52567                     }else{
52568                         this.store.filter(this.displayField, q);
52569                     }
52570                     this.onLoad();
52571                 }else{
52572                     this.store.baseParams[this.queryParam] = q;
52573                     this.store.load({
52574                         params: this.getParams(q)
52575                     });
52576                     this.expand();
52577                 }
52578             }else{
52579                 this.selectedIndex = -1;
52580                 this.onLoad();   
52581             }
52582         }
52583     },
52584
52585     // private
52586     getParams : function(q){
52587         var p = {};
52588         //p[this.queryParam] = q;
52589         if(this.pageSize){
52590             p.start = 0;
52591             p.limit = this.pageSize;
52592         }
52593         return p;
52594     },
52595
52596     /**
52597      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
52598      */
52599     collapse : function(){
52600         
52601     },
52602
52603     // private
52604     collapseIf : function(e){
52605         
52606     },
52607
52608     /**
52609      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
52610      */
52611     expand : function(){
52612         
52613     } ,
52614
52615     // private
52616      
52617
52618     /** 
52619     * @cfg {Boolean} grow 
52620     * @hide 
52621     */
52622     /** 
52623     * @cfg {Number} growMin 
52624     * @hide 
52625     */
52626     /** 
52627     * @cfg {Number} growMax 
52628     * @hide 
52629     */
52630     /**
52631      * @hide
52632      * @method autoSize
52633      */
52634     
52635     setWidth : function()
52636     {
52637         
52638     },
52639     getResizeEl : function(){
52640         return this.el;
52641     }
52642 });//<script type="text/javasscript">
52643  
52644
52645 /**
52646  * @class Roo.DDView
52647  * A DnD enabled version of Roo.View.
52648  * @param {Element/String} container The Element in which to create the View.
52649  * @param {String} tpl The template string used to create the markup for each element of the View
52650  * @param {Object} config The configuration properties. These include all the config options of
52651  * {@link Roo.View} plus some specific to this class.<br>
52652  * <p>
52653  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
52654  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
52655  * <p>
52656  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
52657 .x-view-drag-insert-above {
52658         border-top:1px dotted #3366cc;
52659 }
52660 .x-view-drag-insert-below {
52661         border-bottom:1px dotted #3366cc;
52662 }
52663 </code></pre>
52664  * 
52665  */
52666  
52667 Roo.DDView = function(container, tpl, config) {
52668     Roo.DDView.superclass.constructor.apply(this, arguments);
52669     this.getEl().setStyle("outline", "0px none");
52670     this.getEl().unselectable();
52671     if (this.dragGroup) {
52672         this.setDraggable(this.dragGroup.split(","));
52673     }
52674     if (this.dropGroup) {
52675         this.setDroppable(this.dropGroup.split(","));
52676     }
52677     if (this.deletable) {
52678         this.setDeletable();
52679     }
52680     this.isDirtyFlag = false;
52681         this.addEvents({
52682                 "drop" : true
52683         });
52684 };
52685
52686 Roo.extend(Roo.DDView, Roo.View, {
52687 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
52688 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
52689 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
52690 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
52691
52692         isFormField: true,
52693
52694         reset: Roo.emptyFn,
52695         
52696         clearInvalid: Roo.form.Field.prototype.clearInvalid,
52697
52698         validate: function() {
52699                 return true;
52700         },
52701         
52702         destroy: function() {
52703                 this.purgeListeners();
52704                 this.getEl.removeAllListeners();
52705                 this.getEl().remove();
52706                 if (this.dragZone) {
52707                         if (this.dragZone.destroy) {
52708                                 this.dragZone.destroy();
52709                         }
52710                 }
52711                 if (this.dropZone) {
52712                         if (this.dropZone.destroy) {
52713                                 this.dropZone.destroy();
52714                         }
52715                 }
52716         },
52717
52718 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
52719         getName: function() {
52720                 return this.name;
52721         },
52722
52723 /**     Loads the View from a JSON string representing the Records to put into the Store. */
52724         setValue: function(v) {
52725                 if (!this.store) {
52726                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
52727                 }
52728                 var data = {};
52729                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
52730                 this.store.proxy = new Roo.data.MemoryProxy(data);
52731                 this.store.load();
52732         },
52733
52734 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
52735         getValue: function() {
52736                 var result = '(';
52737                 this.store.each(function(rec) {
52738                         result += rec.id + ',';
52739                 });
52740                 return result.substr(0, result.length - 1) + ')';
52741         },
52742         
52743         getIds: function() {
52744                 var i = 0, result = new Array(this.store.getCount());
52745                 this.store.each(function(rec) {
52746                         result[i++] = rec.id;
52747                 });
52748                 return result;
52749         },
52750         
52751         isDirty: function() {
52752                 return this.isDirtyFlag;
52753         },
52754
52755 /**
52756  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
52757  *      whole Element becomes the target, and this causes the drop gesture to append.
52758  */
52759     getTargetFromEvent : function(e) {
52760                 var target = e.getTarget();
52761                 while ((target !== null) && (target.parentNode != this.el.dom)) {
52762                 target = target.parentNode;
52763                 }
52764                 if (!target) {
52765                         target = this.el.dom.lastChild || this.el.dom;
52766                 }
52767                 return target;
52768     },
52769
52770 /**
52771  *      Create the drag data which consists of an object which has the property "ddel" as
52772  *      the drag proxy element. 
52773  */
52774     getDragData : function(e) {
52775         var target = this.findItemFromChild(e.getTarget());
52776                 if(target) {
52777                         this.handleSelection(e);
52778                         var selNodes = this.getSelectedNodes();
52779             var dragData = {
52780                 source: this,
52781                 copy: this.copy || (this.allowCopy && e.ctrlKey),
52782                 nodes: selNodes,
52783                 records: []
52784                         };
52785                         var selectedIndices = this.getSelectedIndexes();
52786                         for (var i = 0; i < selectedIndices.length; i++) {
52787                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
52788                         }
52789                         if (selNodes.length == 1) {
52790                                 dragData.ddel = target.cloneNode(true); // the div element
52791                         } else {
52792                                 var div = document.createElement('div'); // create the multi element drag "ghost"
52793                                 div.className = 'multi-proxy';
52794                                 for (var i = 0, len = selNodes.length; i < len; i++) {
52795                                         div.appendChild(selNodes[i].cloneNode(true));
52796                                 }
52797                                 dragData.ddel = div;
52798                         }
52799             //console.log(dragData)
52800             //console.log(dragData.ddel.innerHTML)
52801                         return dragData;
52802                 }
52803         //console.log('nodragData')
52804                 return false;
52805     },
52806     
52807 /**     Specify to which ddGroup items in this DDView may be dragged. */
52808     setDraggable: function(ddGroup) {
52809         if (ddGroup instanceof Array) {
52810                 Roo.each(ddGroup, this.setDraggable, this);
52811                 return;
52812         }
52813         if (this.dragZone) {
52814                 this.dragZone.addToGroup(ddGroup);
52815         } else {
52816                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
52817                                 containerScroll: true,
52818                                 ddGroup: ddGroup 
52819
52820                         });
52821 //                      Draggability implies selection. DragZone's mousedown selects the element.
52822                         if (!this.multiSelect) { this.singleSelect = true; }
52823
52824 //                      Wire the DragZone's handlers up to methods in *this*
52825                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
52826                 }
52827     },
52828
52829 /**     Specify from which ddGroup this DDView accepts drops. */
52830     setDroppable: function(ddGroup) {
52831         if (ddGroup instanceof Array) {
52832                 Roo.each(ddGroup, this.setDroppable, this);
52833                 return;
52834         }
52835         if (this.dropZone) {
52836                 this.dropZone.addToGroup(ddGroup);
52837         } else {
52838                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
52839                                 containerScroll: true,
52840                                 ddGroup: ddGroup
52841                         });
52842
52843 //                      Wire the DropZone's handlers up to methods in *this*
52844                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
52845                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
52846                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
52847                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
52848                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
52849                 }
52850     },
52851
52852 /**     Decide whether to drop above or below a View node. */
52853     getDropPoint : function(e, n, dd){
52854         if (n == this.el.dom) { return "above"; }
52855                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
52856                 var c = t + (b - t) / 2;
52857                 var y = Roo.lib.Event.getPageY(e);
52858                 if(y <= c) {
52859                         return "above";
52860                 }else{
52861                         return "below";
52862                 }
52863     },
52864
52865     onNodeEnter : function(n, dd, e, data){
52866                 return false;
52867     },
52868     
52869     onNodeOver : function(n, dd, e, data){
52870                 var pt = this.getDropPoint(e, n, dd);
52871                 // set the insert point style on the target node
52872                 var dragElClass = this.dropNotAllowed;
52873                 if (pt) {
52874                         var targetElClass;
52875                         if (pt == "above"){
52876                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
52877                                 targetElClass = "x-view-drag-insert-above";
52878                         } else {
52879                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
52880                                 targetElClass = "x-view-drag-insert-below";
52881                         }
52882                         if (this.lastInsertClass != targetElClass){
52883                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
52884                                 this.lastInsertClass = targetElClass;
52885                         }
52886                 }
52887                 return dragElClass;
52888         },
52889
52890     onNodeOut : function(n, dd, e, data){
52891                 this.removeDropIndicators(n);
52892     },
52893
52894     onNodeDrop : function(n, dd, e, data){
52895         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
52896                 return false;
52897         }
52898         var pt = this.getDropPoint(e, n, dd);
52899                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
52900                 if (pt == "below") { insertAt++; }
52901                 for (var i = 0; i < data.records.length; i++) {
52902                         var r = data.records[i];
52903                         var dup = this.store.getById(r.id);
52904                         if (dup && (dd != this.dragZone)) {
52905                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
52906                         } else {
52907                                 if (data.copy) {
52908                                         this.store.insert(insertAt++, r.copy());
52909                                 } else {
52910                                         data.source.isDirtyFlag = true;
52911                                         r.store.remove(r);
52912                                         this.store.insert(insertAt++, r);
52913                                 }
52914                                 this.isDirtyFlag = true;
52915                         }
52916                 }
52917                 this.dragZone.cachedTarget = null;
52918                 return true;
52919     },
52920
52921     removeDropIndicators : function(n){
52922                 if(n){
52923                         Roo.fly(n).removeClass([
52924                                 "x-view-drag-insert-above",
52925                                 "x-view-drag-insert-below"]);
52926                         this.lastInsertClass = "_noclass";
52927                 }
52928     },
52929
52930 /**
52931  *      Utility method. Add a delete option to the DDView's context menu.
52932  *      @param {String} imageUrl The URL of the "delete" icon image.
52933  */
52934         setDeletable: function(imageUrl) {
52935                 if (!this.singleSelect && !this.multiSelect) {
52936                         this.singleSelect = true;
52937                 }
52938                 var c = this.getContextMenu();
52939                 this.contextMenu.on("itemclick", function(item) {
52940                         switch (item.id) {
52941                                 case "delete":
52942                                         this.remove(this.getSelectedIndexes());
52943                                         break;
52944                         }
52945                 }, this);
52946                 this.contextMenu.add({
52947                         icon: imageUrl,
52948                         id: "delete",
52949                         text: 'Delete'
52950                 });
52951         },
52952         
52953 /**     Return the context menu for this DDView. */
52954         getContextMenu: function() {
52955                 if (!this.contextMenu) {
52956 //                      Create the View's context menu
52957                         this.contextMenu = new Roo.menu.Menu({
52958                                 id: this.id + "-contextmenu"
52959                         });
52960                         this.el.on("contextmenu", this.showContextMenu, this);
52961                 }
52962                 return this.contextMenu;
52963         },
52964         
52965         disableContextMenu: function() {
52966                 if (this.contextMenu) {
52967                         this.el.un("contextmenu", this.showContextMenu, this);
52968                 }
52969         },
52970
52971         showContextMenu: function(e, item) {
52972         item = this.findItemFromChild(e.getTarget());
52973                 if (item) {
52974                         e.stopEvent();
52975                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
52976                         this.contextMenu.showAt(e.getXY());
52977             }
52978     },
52979
52980 /**
52981  *      Remove {@link Roo.data.Record}s at the specified indices.
52982  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
52983  */
52984     remove: function(selectedIndices) {
52985                 selectedIndices = [].concat(selectedIndices);
52986                 for (var i = 0; i < selectedIndices.length; i++) {
52987                         var rec = this.store.getAt(selectedIndices[i]);
52988                         this.store.remove(rec);
52989                 }
52990     },
52991
52992 /**
52993  *      Double click fires the event, but also, if this is draggable, and there is only one other
52994  *      related DropZone, it transfers the selected node.
52995  */
52996     onDblClick : function(e){
52997         var item = this.findItemFromChild(e.getTarget());
52998         if(item){
52999             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
53000                 return false;
53001             }
53002             if (this.dragGroup) {
53003                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
53004                     while (targets.indexOf(this.dropZone) > -1) {
53005                             targets.remove(this.dropZone);
53006                                 }
53007                     if (targets.length == 1) {
53008                                         this.dragZone.cachedTarget = null;
53009                         var el = Roo.get(targets[0].getEl());
53010                         var box = el.getBox(true);
53011                         targets[0].onNodeDrop(el.dom, {
53012                                 target: el.dom,
53013                                 xy: [box.x, box.y + box.height - 1]
53014                         }, null, this.getDragData(e));
53015                     }
53016                 }
53017         }
53018     },
53019     
53020     handleSelection: function(e) {
53021                 this.dragZone.cachedTarget = null;
53022         var item = this.findItemFromChild(e.getTarget());
53023         if (!item) {
53024                 this.clearSelections(true);
53025                 return;
53026         }
53027                 if (item && (this.multiSelect || this.singleSelect)){
53028                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
53029                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
53030                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
53031                                 this.unselect(item);
53032                         } else {
53033                                 this.select(item, this.multiSelect && e.ctrlKey);
53034                                 this.lastSelection = item;
53035                         }
53036                 }
53037     },
53038
53039     onItemClick : function(item, index, e){
53040                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
53041                         return false;
53042                 }
53043                 return true;
53044     },
53045
53046     unselect : function(nodeInfo, suppressEvent){
53047                 var node = this.getNode(nodeInfo);
53048                 if(node && this.isSelected(node)){
53049                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
53050                                 Roo.fly(node).removeClass(this.selectedClass);
53051                                 this.selections.remove(node);
53052                                 if(!suppressEvent){
53053                                         this.fireEvent("selectionchange", this, this.selections);
53054                                 }
53055                         }
53056                 }
53057     }
53058 });
53059 /*
53060  * Based on:
53061  * Ext JS Library 1.1.1
53062  * Copyright(c) 2006-2007, Ext JS, LLC.
53063  *
53064  * Originally Released Under LGPL - original licence link has changed is not relivant.
53065  *
53066  * Fork - LGPL
53067  * <script type="text/javascript">
53068  */
53069  
53070 /**
53071  * @class Roo.LayoutManager
53072  * @extends Roo.util.Observable
53073  * Base class for layout managers.
53074  */
53075 Roo.LayoutManager = function(container, config){
53076     Roo.LayoutManager.superclass.constructor.call(this);
53077     this.el = Roo.get(container);
53078     // ie scrollbar fix
53079     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
53080         document.body.scroll = "no";
53081     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
53082         this.el.position('relative');
53083     }
53084     this.id = this.el.id;
53085     this.el.addClass("x-layout-container");
53086     /** false to disable window resize monitoring @type Boolean */
53087     this.monitorWindowResize = true;
53088     this.regions = {};
53089     this.addEvents({
53090         /**
53091          * @event layout
53092          * Fires when a layout is performed. 
53093          * @param {Roo.LayoutManager} this
53094          */
53095         "layout" : true,
53096         /**
53097          * @event regionresized
53098          * Fires when the user resizes a region. 
53099          * @param {Roo.LayoutRegion} region The resized region
53100          * @param {Number} newSize The new size (width for east/west, height for north/south)
53101          */
53102         "regionresized" : true,
53103         /**
53104          * @event regioncollapsed
53105          * Fires when a region is collapsed. 
53106          * @param {Roo.LayoutRegion} region The collapsed region
53107          */
53108         "regioncollapsed" : true,
53109         /**
53110          * @event regionexpanded
53111          * Fires when a region is expanded.  
53112          * @param {Roo.LayoutRegion} region The expanded region
53113          */
53114         "regionexpanded" : true
53115     });
53116     this.updating = false;
53117     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53118 };
53119
53120 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
53121     /**
53122      * Returns true if this layout is currently being updated
53123      * @return {Boolean}
53124      */
53125     isUpdating : function(){
53126         return this.updating; 
53127     },
53128     
53129     /**
53130      * Suspend the LayoutManager from doing auto-layouts while
53131      * making multiple add or remove calls
53132      */
53133     beginUpdate : function(){
53134         this.updating = true;    
53135     },
53136     
53137     /**
53138      * Restore auto-layouts and optionally disable the manager from performing a layout
53139      * @param {Boolean} noLayout true to disable a layout update 
53140      */
53141     endUpdate : function(noLayout){
53142         this.updating = false;
53143         if(!noLayout){
53144             this.layout();
53145         }    
53146     },
53147     
53148     layout: function(){
53149         
53150     },
53151     
53152     onRegionResized : function(region, newSize){
53153         this.fireEvent("regionresized", region, newSize);
53154         this.layout();
53155     },
53156     
53157     onRegionCollapsed : function(region){
53158         this.fireEvent("regioncollapsed", region);
53159     },
53160     
53161     onRegionExpanded : function(region){
53162         this.fireEvent("regionexpanded", region);
53163     },
53164         
53165     /**
53166      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
53167      * performs box-model adjustments.
53168      * @return {Object} The size as an object {width: (the width), height: (the height)}
53169      */
53170     getViewSize : function(){
53171         var size;
53172         if(this.el.dom != document.body){
53173             size = this.el.getSize();
53174         }else{
53175             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
53176         }
53177         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
53178         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
53179         return size;
53180     },
53181     
53182     /**
53183      * Returns the Element this layout is bound to.
53184      * @return {Roo.Element}
53185      */
53186     getEl : function(){
53187         return this.el;
53188     },
53189     
53190     /**
53191      * Returns the specified region.
53192      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
53193      * @return {Roo.LayoutRegion}
53194      */
53195     getRegion : function(target){
53196         return this.regions[target.toLowerCase()];
53197     },
53198     
53199     onWindowResize : function(){
53200         if(this.monitorWindowResize){
53201             this.layout();
53202         }
53203     }
53204 });/*
53205  * Based on:
53206  * Ext JS Library 1.1.1
53207  * Copyright(c) 2006-2007, Ext JS, LLC.
53208  *
53209  * Originally Released Under LGPL - original licence link has changed is not relivant.
53210  *
53211  * Fork - LGPL
53212  * <script type="text/javascript">
53213  */
53214 /**
53215  * @class Roo.BorderLayout
53216  * @extends Roo.LayoutManager
53217  * @children Roo.ContentPanel
53218  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
53219  * please see: <br><br>
53220  * <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>
53221  * <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>
53222  * Example:
53223  <pre><code>
53224  var layout = new Roo.BorderLayout(document.body, {
53225     north: {
53226         initialSize: 25,
53227         titlebar: false
53228     },
53229     west: {
53230         split:true,
53231         initialSize: 200,
53232         minSize: 175,
53233         maxSize: 400,
53234         titlebar: true,
53235         collapsible: true
53236     },
53237     east: {
53238         split:true,
53239         initialSize: 202,
53240         minSize: 175,
53241         maxSize: 400,
53242         titlebar: true,
53243         collapsible: true
53244     },
53245     south: {
53246         split:true,
53247         initialSize: 100,
53248         minSize: 100,
53249         maxSize: 200,
53250         titlebar: true,
53251         collapsible: true
53252     },
53253     center: {
53254         titlebar: true,
53255         autoScroll:true,
53256         resizeTabs: true,
53257         minTabWidth: 50,
53258         preferredTabWidth: 150
53259     }
53260 });
53261
53262 // shorthand
53263 var CP = Roo.ContentPanel;
53264
53265 layout.beginUpdate();
53266 layout.add("north", new CP("north", "North"));
53267 layout.add("south", new CP("south", {title: "South", closable: true}));
53268 layout.add("west", new CP("west", {title: "West"}));
53269 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
53270 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
53271 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53272 layout.getRegion("center").showPanel("center1");
53273 layout.endUpdate();
53274 </code></pre>
53275
53276 <b>The container the layout is rendered into can be either the body element or any other element.
53277 If it is not the body element, the container needs to either be an absolute positioned element,
53278 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53279 the container size if it is not the body element.</b>
53280
53281 * @constructor
53282 * Create a new BorderLayout
53283 * @param {String/HTMLElement/Element} container The container this layout is bound to
53284 * @param {Object} config Configuration options
53285  */
53286 Roo.BorderLayout = function(container, config){
53287     config = config || {};
53288     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53289     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53290     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53291         var target = this.factory.validRegions[i];
53292         if(config[target]){
53293             this.addRegion(target, config[target]);
53294         }
53295     }
53296 };
53297
53298 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53299         
53300         /**
53301          * @cfg {Roo.LayoutRegion} east
53302          */
53303         /**
53304          * @cfg {Roo.LayoutRegion} west
53305          */
53306         /**
53307          * @cfg {Roo.LayoutRegion} north
53308          */
53309         /**
53310          * @cfg {Roo.LayoutRegion} south
53311          */
53312         /**
53313          * @cfg {Roo.LayoutRegion} center
53314          */
53315     /**
53316      * Creates and adds a new region if it doesn't already exist.
53317      * @param {String} target The target region key (north, south, east, west or center).
53318      * @param {Object} config The regions config object
53319      * @return {BorderLayoutRegion} The new region
53320      */
53321     addRegion : function(target, config){
53322         if(!this.regions[target]){
53323             var r = this.factory.create(target, this, config);
53324             this.bindRegion(target, r);
53325         }
53326         return this.regions[target];
53327     },
53328
53329     // private (kinda)
53330     bindRegion : function(name, r){
53331         this.regions[name] = r;
53332         r.on("visibilitychange", this.layout, this);
53333         r.on("paneladded", this.layout, this);
53334         r.on("panelremoved", this.layout, this);
53335         r.on("invalidated", this.layout, this);
53336         r.on("resized", this.onRegionResized, this);
53337         r.on("collapsed", this.onRegionCollapsed, this);
53338         r.on("expanded", this.onRegionExpanded, this);
53339     },
53340
53341     /**
53342      * Performs a layout update.
53343      */
53344     layout : function(){
53345         if(this.updating) {
53346             return;
53347         }
53348         var size = this.getViewSize();
53349         var w = size.width;
53350         var h = size.height;
53351         var centerW = w;
53352         var centerH = h;
53353         var centerY = 0;
53354         var centerX = 0;
53355         //var x = 0, y = 0;
53356
53357         var rs = this.regions;
53358         var north = rs["north"];
53359         var south = rs["south"]; 
53360         var west = rs["west"];
53361         var east = rs["east"];
53362         var center = rs["center"];
53363         //if(this.hideOnLayout){ // not supported anymore
53364             //c.el.setStyle("display", "none");
53365         //}
53366         if(north && north.isVisible()){
53367             var b = north.getBox();
53368             var m = north.getMargins();
53369             b.width = w - (m.left+m.right);
53370             b.x = m.left;
53371             b.y = m.top;
53372             centerY = b.height + b.y + m.bottom;
53373             centerH -= centerY;
53374             north.updateBox(this.safeBox(b));
53375         }
53376         if(south && south.isVisible()){
53377             var b = south.getBox();
53378             var m = south.getMargins();
53379             b.width = w - (m.left+m.right);
53380             b.x = m.left;
53381             var totalHeight = (b.height + m.top + m.bottom);
53382             b.y = h - totalHeight + m.top;
53383             centerH -= totalHeight;
53384             south.updateBox(this.safeBox(b));
53385         }
53386         if(west && west.isVisible()){
53387             var b = west.getBox();
53388             var m = west.getMargins();
53389             b.height = centerH - (m.top+m.bottom);
53390             b.x = m.left;
53391             b.y = centerY + m.top;
53392             var totalWidth = (b.width + m.left + m.right);
53393             centerX += totalWidth;
53394             centerW -= totalWidth;
53395             west.updateBox(this.safeBox(b));
53396         }
53397         if(east && east.isVisible()){
53398             var b = east.getBox();
53399             var m = east.getMargins();
53400             b.height = centerH - (m.top+m.bottom);
53401             var totalWidth = (b.width + m.left + m.right);
53402             b.x = w - totalWidth + m.left;
53403             b.y = centerY + m.top;
53404             centerW -= totalWidth;
53405             east.updateBox(this.safeBox(b));
53406         }
53407         if(center){
53408             var m = center.getMargins();
53409             var centerBox = {
53410                 x: centerX + m.left,
53411                 y: centerY + m.top,
53412                 width: centerW - (m.left+m.right),
53413                 height: centerH - (m.top+m.bottom)
53414             };
53415             //if(this.hideOnLayout){
53416                 //center.el.setStyle("display", "block");
53417             //}
53418             center.updateBox(this.safeBox(centerBox));
53419         }
53420         this.el.repaint();
53421         this.fireEvent("layout", this);
53422     },
53423
53424     // private
53425     safeBox : function(box){
53426         box.width = Math.max(0, box.width);
53427         box.height = Math.max(0, box.height);
53428         return box;
53429     },
53430
53431     /**
53432      * Adds a ContentPanel (or subclass) to this layout.
53433      * @param {String} target The target region key (north, south, east, west or center).
53434      * @param {Roo.ContentPanel} panel The panel to add
53435      * @return {Roo.ContentPanel} The added panel
53436      */
53437     add : function(target, panel){
53438          
53439         target = target.toLowerCase();
53440         return this.regions[target].add(panel);
53441     },
53442
53443     /**
53444      * Remove a ContentPanel (or subclass) to this layout.
53445      * @param {String} target The target region key (north, south, east, west or center).
53446      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
53447      * @return {Roo.ContentPanel} The removed panel
53448      */
53449     remove : function(target, panel){
53450         target = target.toLowerCase();
53451         return this.regions[target].remove(panel);
53452     },
53453
53454     /**
53455      * Searches all regions for a panel with the specified id
53456      * @param {String} panelId
53457      * @return {Roo.ContentPanel} The panel or null if it wasn't found
53458      */
53459     findPanel : function(panelId){
53460         var rs = this.regions;
53461         for(var target in rs){
53462             if(typeof rs[target] != "function"){
53463                 var p = rs[target].getPanel(panelId);
53464                 if(p){
53465                     return p;
53466                 }
53467             }
53468         }
53469         return null;
53470     },
53471
53472     /**
53473      * Searches all regions for a panel with the specified id and activates (shows) it.
53474      * @param {String/ContentPanel} panelId The panels id or the panel itself
53475      * @return {Roo.ContentPanel} The shown panel or null
53476      */
53477     showPanel : function(panelId) {
53478       var rs = this.regions;
53479       for(var target in rs){
53480          var r = rs[target];
53481          if(typeof r != "function"){
53482             if(r.hasPanel(panelId)){
53483                return r.showPanel(panelId);
53484             }
53485          }
53486       }
53487       return null;
53488    },
53489
53490    /**
53491      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
53492      * @param {Roo.state.Provider} provider (optional) An alternate state provider
53493      */
53494     restoreState : function(provider){
53495         if(!provider){
53496             provider = Roo.state.Manager;
53497         }
53498         var sm = new Roo.LayoutStateManager();
53499         sm.init(this, provider);
53500     },
53501
53502     /**
53503      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
53504      * object should contain properties for each region to add ContentPanels to, and each property's value should be
53505      * a valid ContentPanel config object.  Example:
53506      * <pre><code>
53507 // Create the main layout
53508 var layout = new Roo.BorderLayout('main-ct', {
53509     west: {
53510         split:true,
53511         minSize: 175,
53512         titlebar: true
53513     },
53514     center: {
53515         title:'Components'
53516     }
53517 }, 'main-ct');
53518
53519 // Create and add multiple ContentPanels at once via configs
53520 layout.batchAdd({
53521    west: {
53522        id: 'source-files',
53523        autoCreate:true,
53524        title:'Ext Source Files',
53525        autoScroll:true,
53526        fitToFrame:true
53527    },
53528    center : {
53529        el: cview,
53530        autoScroll:true,
53531        fitToFrame:true,
53532        toolbar: tb,
53533        resizeEl:'cbody'
53534    }
53535 });
53536 </code></pre>
53537      * @param {Object} regions An object containing ContentPanel configs by region name
53538      */
53539     batchAdd : function(regions){
53540         this.beginUpdate();
53541         for(var rname in regions){
53542             var lr = this.regions[rname];
53543             if(lr){
53544                 this.addTypedPanels(lr, regions[rname]);
53545             }
53546         }
53547         this.endUpdate();
53548     },
53549
53550     // private
53551     addTypedPanels : function(lr, ps){
53552         if(typeof ps == 'string'){
53553             lr.add(new Roo.ContentPanel(ps));
53554         }
53555         else if(ps instanceof Array){
53556             for(var i =0, len = ps.length; i < len; i++){
53557                 this.addTypedPanels(lr, ps[i]);
53558             }
53559         }
53560         else if(!ps.events){ // raw config?
53561             var el = ps.el;
53562             delete ps.el; // prevent conflict
53563             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
53564         }
53565         else {  // panel object assumed!
53566             lr.add(ps);
53567         }
53568     },
53569     /**
53570      * Adds a xtype elements to the layout.
53571      * <pre><code>
53572
53573 layout.addxtype({
53574        xtype : 'ContentPanel',
53575        region: 'west',
53576        items: [ .... ]
53577    }
53578 );
53579
53580 layout.addxtype({
53581         xtype : 'NestedLayoutPanel',
53582         region: 'west',
53583         layout: {
53584            center: { },
53585            west: { }   
53586         },
53587         items : [ ... list of content panels or nested layout panels.. ]
53588    }
53589 );
53590 </code></pre>
53591      * @param {Object} cfg Xtype definition of item to add.
53592      */
53593     addxtype : function(cfg)
53594     {
53595         // basically accepts a pannel...
53596         // can accept a layout region..!?!?
53597         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
53598         
53599         if (!cfg.xtype.match(/Panel$/)) {
53600             return false;
53601         }
53602         var ret = false;
53603         
53604         if (typeof(cfg.region) == 'undefined') {
53605             Roo.log("Failed to add Panel, region was not set");
53606             Roo.log(cfg);
53607             return false;
53608         }
53609         var region = cfg.region;
53610         delete cfg.region;
53611         
53612           
53613         var xitems = [];
53614         if (cfg.items) {
53615             xitems = cfg.items;
53616             delete cfg.items;
53617         }
53618         var nb = false;
53619         
53620         switch(cfg.xtype) 
53621         {
53622             case 'ContentPanel':  // ContentPanel (el, cfg)
53623             case 'ScrollPanel':  // ContentPanel (el, cfg)
53624             case 'ViewPanel': 
53625                 if(cfg.autoCreate) {
53626                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53627                 } else {
53628                     var el = this.el.createChild();
53629                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
53630                 }
53631                 
53632                 this.add(region, ret);
53633                 break;
53634             
53635             
53636             case 'TreePanel': // our new panel!
53637                 cfg.el = this.el.createChild();
53638                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53639                 this.add(region, ret);
53640                 break;
53641             
53642             case 'NestedLayoutPanel': 
53643                 // create a new Layout (which is  a Border Layout...
53644                 var el = this.el.createChild();
53645                 var clayout = cfg.layout;
53646                 delete cfg.layout;
53647                 clayout.items   = clayout.items  || [];
53648                 // replace this exitems with the clayout ones..
53649                 xitems = clayout.items;
53650                  
53651                 
53652                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
53653                     cfg.background = false;
53654                 }
53655                 var layout = new Roo.BorderLayout(el, clayout);
53656                 
53657                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
53658                 //console.log('adding nested layout panel '  + cfg.toSource());
53659                 this.add(region, ret);
53660                 nb = {}; /// find first...
53661                 break;
53662                 
53663             case 'GridPanel': 
53664             
53665                 // needs grid and region
53666                 
53667                 //var el = this.getRegion(region).el.createChild();
53668                 var el = this.el.createChild();
53669                 // create the grid first...
53670                 
53671                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
53672                 delete cfg.grid;
53673                 if (region == 'center' && this.active ) {
53674                     cfg.background = false;
53675                 }
53676                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
53677                 
53678                 this.add(region, ret);
53679                 if (cfg.background) {
53680                     ret.on('activate', function(gp) {
53681                         if (!gp.grid.rendered) {
53682                             gp.grid.render();
53683                         }
53684                     });
53685                 } else {
53686                     grid.render();
53687                 }
53688                 break;
53689            
53690            
53691            
53692                 
53693                 
53694                 
53695             default:
53696                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
53697                     
53698                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53699                     this.add(region, ret);
53700                 } else {
53701                 
53702                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
53703                     return null;
53704                 }
53705                 
53706              // GridPanel (grid, cfg)
53707             
53708         }
53709         this.beginUpdate();
53710         // add children..
53711         var region = '';
53712         var abn = {};
53713         Roo.each(xitems, function(i)  {
53714             region = nb && i.region ? i.region : false;
53715             
53716             var add = ret.addxtype(i);
53717            
53718             if (region) {
53719                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
53720                 if (!i.background) {
53721                     abn[region] = nb[region] ;
53722                 }
53723             }
53724             
53725         });
53726         this.endUpdate();
53727
53728         // make the last non-background panel active..
53729         //if (nb) { Roo.log(abn); }
53730         if (nb) {
53731             
53732             for(var r in abn) {
53733                 region = this.getRegion(r);
53734                 if (region) {
53735                     // tried using nb[r], but it does not work..
53736                      
53737                     region.showPanel(abn[r]);
53738                    
53739                 }
53740             }
53741         }
53742         return ret;
53743         
53744     }
53745 });
53746
53747 /**
53748  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
53749  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
53750  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
53751  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
53752  * <pre><code>
53753 // shorthand
53754 var CP = Roo.ContentPanel;
53755
53756 var layout = Roo.BorderLayout.create({
53757     north: {
53758         initialSize: 25,
53759         titlebar: false,
53760         panels: [new CP("north", "North")]
53761     },
53762     west: {
53763         split:true,
53764         initialSize: 200,
53765         minSize: 175,
53766         maxSize: 400,
53767         titlebar: true,
53768         collapsible: true,
53769         panels: [new CP("west", {title: "West"})]
53770     },
53771     east: {
53772         split:true,
53773         initialSize: 202,
53774         minSize: 175,
53775         maxSize: 400,
53776         titlebar: true,
53777         collapsible: true,
53778         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
53779     },
53780     south: {
53781         split:true,
53782         initialSize: 100,
53783         minSize: 100,
53784         maxSize: 200,
53785         titlebar: true,
53786         collapsible: true,
53787         panels: [new CP("south", {title: "South", closable: true})]
53788     },
53789     center: {
53790         titlebar: true,
53791         autoScroll:true,
53792         resizeTabs: true,
53793         minTabWidth: 50,
53794         preferredTabWidth: 150,
53795         panels: [
53796             new CP("center1", {title: "Close Me", closable: true}),
53797             new CP("center2", {title: "Center Panel", closable: false})
53798         ]
53799     }
53800 }, document.body);
53801
53802 layout.getRegion("center").showPanel("center1");
53803 </code></pre>
53804  * @param config
53805  * @param targetEl
53806  */
53807 Roo.BorderLayout.create = function(config, targetEl){
53808     var layout = new Roo.BorderLayout(targetEl || document.body, config);
53809     layout.beginUpdate();
53810     var regions = Roo.BorderLayout.RegionFactory.validRegions;
53811     for(var j = 0, jlen = regions.length; j < jlen; j++){
53812         var lr = regions[j];
53813         if(layout.regions[lr] && config[lr].panels){
53814             var r = layout.regions[lr];
53815             var ps = config[lr].panels;
53816             layout.addTypedPanels(r, ps);
53817         }
53818     }
53819     layout.endUpdate();
53820     return layout;
53821 };
53822
53823 // private
53824 Roo.BorderLayout.RegionFactory = {
53825     // private
53826     validRegions : ["north","south","east","west","center"],
53827
53828     // private
53829     create : function(target, mgr, config){
53830         target = target.toLowerCase();
53831         if(config.lightweight || config.basic){
53832             return new Roo.BasicLayoutRegion(mgr, config, target);
53833         }
53834         switch(target){
53835             case "north":
53836                 return new Roo.NorthLayoutRegion(mgr, config);
53837             case "south":
53838                 return new Roo.SouthLayoutRegion(mgr, config);
53839             case "east":
53840                 return new Roo.EastLayoutRegion(mgr, config);
53841             case "west":
53842                 return new Roo.WestLayoutRegion(mgr, config);
53843             case "center":
53844                 return new Roo.CenterLayoutRegion(mgr, config);
53845         }
53846         throw 'Layout region "'+target+'" not supported.';
53847     }
53848 };/*
53849  * Based on:
53850  * Ext JS Library 1.1.1
53851  * Copyright(c) 2006-2007, Ext JS, LLC.
53852  *
53853  * Originally Released Under LGPL - original licence link has changed is not relivant.
53854  *
53855  * Fork - LGPL
53856  * <script type="text/javascript">
53857  */
53858  
53859 /**
53860  * @class Roo.BasicLayoutRegion
53861  * @extends Roo.util.Observable
53862  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
53863  * and does not have a titlebar, tabs or any other features. All it does is size and position 
53864  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
53865  */
53866 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
53867     this.mgr = mgr;
53868     this.position  = pos;
53869     this.events = {
53870         /**
53871          * @scope Roo.BasicLayoutRegion
53872          */
53873         
53874         /**
53875          * @event beforeremove
53876          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
53877          * @param {Roo.LayoutRegion} this
53878          * @param {Roo.ContentPanel} panel The panel
53879          * @param {Object} e The cancel event object
53880          */
53881         "beforeremove" : true,
53882         /**
53883          * @event invalidated
53884          * Fires when the layout for this region is changed.
53885          * @param {Roo.LayoutRegion} this
53886          */
53887         "invalidated" : true,
53888         /**
53889          * @event visibilitychange
53890          * Fires when this region is shown or hidden 
53891          * @param {Roo.LayoutRegion} this
53892          * @param {Boolean} visibility true or false
53893          */
53894         "visibilitychange" : true,
53895         /**
53896          * @event paneladded
53897          * Fires when a panel is added. 
53898          * @param {Roo.LayoutRegion} this
53899          * @param {Roo.ContentPanel} panel The panel
53900          */
53901         "paneladded" : true,
53902         /**
53903          * @event panelremoved
53904          * Fires when a panel is removed. 
53905          * @param {Roo.LayoutRegion} this
53906          * @param {Roo.ContentPanel} panel The panel
53907          */
53908         "panelremoved" : true,
53909         /**
53910          * @event beforecollapse
53911          * Fires when this region before collapse.
53912          * @param {Roo.LayoutRegion} this
53913          */
53914         "beforecollapse" : true,
53915         /**
53916          * @event collapsed
53917          * Fires when this region is collapsed.
53918          * @param {Roo.LayoutRegion} this
53919          */
53920         "collapsed" : true,
53921         /**
53922          * @event expanded
53923          * Fires when this region is expanded.
53924          * @param {Roo.LayoutRegion} this
53925          */
53926         "expanded" : true,
53927         /**
53928          * @event slideshow
53929          * Fires when this region is slid into view.
53930          * @param {Roo.LayoutRegion} this
53931          */
53932         "slideshow" : true,
53933         /**
53934          * @event slidehide
53935          * Fires when this region slides out of view. 
53936          * @param {Roo.LayoutRegion} this
53937          */
53938         "slidehide" : true,
53939         /**
53940          * @event panelactivated
53941          * Fires when a panel is activated. 
53942          * @param {Roo.LayoutRegion} this
53943          * @param {Roo.ContentPanel} panel The activated panel
53944          */
53945         "panelactivated" : true,
53946         /**
53947          * @event resized
53948          * Fires when the user resizes this region. 
53949          * @param {Roo.LayoutRegion} this
53950          * @param {Number} newSize The new size (width for east/west, height for north/south)
53951          */
53952         "resized" : true
53953     };
53954     /** A collection of panels in this region. @type Roo.util.MixedCollection */
53955     this.panels = new Roo.util.MixedCollection();
53956     this.panels.getKey = this.getPanelId.createDelegate(this);
53957     this.box = null;
53958     this.activePanel = null;
53959     // ensure listeners are added...
53960     
53961     if (config.listeners || config.events) {
53962         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
53963             listeners : config.listeners || {},
53964             events : config.events || {}
53965         });
53966     }
53967     
53968     if(skipConfig !== true){
53969         this.applyConfig(config);
53970     }
53971 };
53972
53973 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
53974     getPanelId : function(p){
53975         return p.getId();
53976     },
53977     
53978     applyConfig : function(config){
53979         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53980         this.config = config;
53981         
53982     },
53983     
53984     /**
53985      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
53986      * the width, for horizontal (north, south) the height.
53987      * @param {Number} newSize The new width or height
53988      */
53989     resizeTo : function(newSize){
53990         var el = this.el ? this.el :
53991                  (this.activePanel ? this.activePanel.getEl() : null);
53992         if(el){
53993             switch(this.position){
53994                 case "east":
53995                 case "west":
53996                     el.setWidth(newSize);
53997                     this.fireEvent("resized", this, newSize);
53998                 break;
53999                 case "north":
54000                 case "south":
54001                     el.setHeight(newSize);
54002                     this.fireEvent("resized", this, newSize);
54003                 break;                
54004             }
54005         }
54006     },
54007     
54008     getBox : function(){
54009         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
54010     },
54011     
54012     getMargins : function(){
54013         return this.margins;
54014     },
54015     
54016     updateBox : function(box){
54017         this.box = box;
54018         var el = this.activePanel.getEl();
54019         el.dom.style.left = box.x + "px";
54020         el.dom.style.top = box.y + "px";
54021         this.activePanel.setSize(box.width, box.height);
54022     },
54023     
54024     /**
54025      * Returns the container element for this region.
54026      * @return {Roo.Element}
54027      */
54028     getEl : function(){
54029         return this.activePanel;
54030     },
54031     
54032     /**
54033      * Returns true if this region is currently visible.
54034      * @return {Boolean}
54035      */
54036     isVisible : function(){
54037         return this.activePanel ? true : false;
54038     },
54039     
54040     setActivePanel : function(panel){
54041         panel = this.getPanel(panel);
54042         if(this.activePanel && this.activePanel != panel){
54043             this.activePanel.setActiveState(false);
54044             this.activePanel.getEl().setLeftTop(-10000,-10000);
54045         }
54046         this.activePanel = panel;
54047         panel.setActiveState(true);
54048         if(this.box){
54049             panel.setSize(this.box.width, this.box.height);
54050         }
54051         this.fireEvent("panelactivated", this, panel);
54052         this.fireEvent("invalidated");
54053     },
54054     
54055     /**
54056      * Show the specified panel.
54057      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
54058      * @return {Roo.ContentPanel} The shown panel or null
54059      */
54060     showPanel : function(panel){
54061         if(panel = this.getPanel(panel)){
54062             this.setActivePanel(panel);
54063         }
54064         return panel;
54065     },
54066     
54067     /**
54068      * Get the active panel for this region.
54069      * @return {Roo.ContentPanel} The active panel or null
54070      */
54071     getActivePanel : function(){
54072         return this.activePanel;
54073     },
54074     
54075     /**
54076      * Add the passed ContentPanel(s)
54077      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54078      * @return {Roo.ContentPanel} The panel added (if only one was added)
54079      */
54080     add : function(panel){
54081         if(arguments.length > 1){
54082             for(var i = 0, len = arguments.length; i < len; i++) {
54083                 this.add(arguments[i]);
54084             }
54085             return null;
54086         }
54087         if(this.hasPanel(panel)){
54088             this.showPanel(panel);
54089             return panel;
54090         }
54091         var el = panel.getEl();
54092         if(el.dom.parentNode != this.mgr.el.dom){
54093             this.mgr.el.dom.appendChild(el.dom);
54094         }
54095         if(panel.setRegion){
54096             panel.setRegion(this);
54097         }
54098         this.panels.add(panel);
54099         el.setStyle("position", "absolute");
54100         if(!panel.background){
54101             this.setActivePanel(panel);
54102             if(this.config.initialSize && this.panels.getCount()==1){
54103                 this.resizeTo(this.config.initialSize);
54104             }
54105         }
54106         this.fireEvent("paneladded", this, panel);
54107         return panel;
54108     },
54109     
54110     /**
54111      * Returns true if the panel is in this region.
54112      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54113      * @return {Boolean}
54114      */
54115     hasPanel : function(panel){
54116         if(typeof panel == "object"){ // must be panel obj
54117             panel = panel.getId();
54118         }
54119         return this.getPanel(panel) ? true : false;
54120     },
54121     
54122     /**
54123      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54124      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54125      * @param {Boolean} preservePanel Overrides the config preservePanel option
54126      * @return {Roo.ContentPanel} The panel that was removed
54127      */
54128     remove : function(panel, preservePanel){
54129         panel = this.getPanel(panel);
54130         if(!panel){
54131             return null;
54132         }
54133         var e = {};
54134         this.fireEvent("beforeremove", this, panel, e);
54135         if(e.cancel === true){
54136             return null;
54137         }
54138         var panelId = panel.getId();
54139         this.panels.removeKey(panelId);
54140         return panel;
54141     },
54142     
54143     /**
54144      * Returns the panel specified or null if it's not in this region.
54145      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54146      * @return {Roo.ContentPanel}
54147      */
54148     getPanel : function(id){
54149         if(typeof id == "object"){ // must be panel obj
54150             return id;
54151         }
54152         return this.panels.get(id);
54153     },
54154     
54155     /**
54156      * Returns this regions position (north/south/east/west/center).
54157      * @return {String} 
54158      */
54159     getPosition: function(){
54160         return this.position;    
54161     }
54162 });/*
54163  * Based on:
54164  * Ext JS Library 1.1.1
54165  * Copyright(c) 2006-2007, Ext JS, LLC.
54166  *
54167  * Originally Released Under LGPL - original licence link has changed is not relivant.
54168  *
54169  * Fork - LGPL
54170  * <script type="text/javascript">
54171  */
54172  
54173 /**
54174  * @class Roo.LayoutRegion
54175  * @extends Roo.BasicLayoutRegion
54176  * This class represents a region in a layout manager.
54177  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
54178  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
54179  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
54180  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
54181  * @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})
54182  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
54183  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
54184  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
54185  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
54186  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
54187  * @cfg {String}    title           The title for the region (overrides panel titles)
54188  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
54189  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
54190  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
54191  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
54192  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
54193  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
54194  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
54195  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
54196  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
54197  * @cfg {Boolean}   showPin         True to show a pin button
54198  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
54199  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
54200  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
54201  * @cfg {Number}    width           For East/West panels
54202  * @cfg {Number}    height          For North/South panels
54203  * @cfg {Boolean}   split           To show the splitter
54204  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
54205  */
54206 Roo.LayoutRegion = function(mgr, config, pos){
54207     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
54208     var dh = Roo.DomHelper;
54209     /** This region's container element 
54210     * @type Roo.Element */
54211     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
54212     /** This region's title element 
54213     * @type Roo.Element */
54214
54215     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
54216         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
54217         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
54218     ]}, true);
54219     this.titleEl.enableDisplayMode();
54220     /** This region's title text element 
54221     * @type HTMLElement */
54222     this.titleTextEl = this.titleEl.dom.firstChild;
54223     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
54224     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
54225     this.closeBtn.enableDisplayMode();
54226     this.closeBtn.on("click", this.closeClicked, this);
54227     this.closeBtn.hide();
54228
54229     this.createBody(config);
54230     this.visible = true;
54231     this.collapsed = false;
54232
54233     if(config.hideWhenEmpty){
54234         this.hide();
54235         this.on("paneladded", this.validateVisibility, this);
54236         this.on("panelremoved", this.validateVisibility, this);
54237     }
54238     this.applyConfig(config);
54239 };
54240
54241 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
54242
54243     createBody : function(){
54244         /** This region's body element 
54245         * @type Roo.Element */
54246         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
54247     },
54248
54249     applyConfig : function(c){
54250         if(c.collapsible && this.position != "center" && !this.collapsedEl){
54251             var dh = Roo.DomHelper;
54252             if(c.titlebar !== false){
54253                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
54254                 this.collapseBtn.on("click", this.collapse, this);
54255                 this.collapseBtn.enableDisplayMode();
54256
54257                 if(c.showPin === true || this.showPin){
54258                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
54259                     this.stickBtn.enableDisplayMode();
54260                     this.stickBtn.on("click", this.expand, this);
54261                     this.stickBtn.hide();
54262                 }
54263             }
54264             /** This region's collapsed element
54265             * @type Roo.Element */
54266             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
54267                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
54268             ]}, true);
54269             if(c.floatable !== false){
54270                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
54271                this.collapsedEl.on("click", this.collapseClick, this);
54272             }
54273
54274             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54275                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54276                    id: "message", unselectable: "on", style:{"float":"left"}});
54277                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54278              }
54279             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54280             this.expandBtn.on("click", this.expand, this);
54281         }
54282         if(this.collapseBtn){
54283             this.collapseBtn.setVisible(c.collapsible == true);
54284         }
54285         this.cmargins = c.cmargins || this.cmargins ||
54286                          (this.position == "west" || this.position == "east" ?
54287                              {top: 0, left: 2, right:2, bottom: 0} :
54288                              {top: 2, left: 0, right:0, bottom: 2});
54289         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54290         this.bottomTabs = c.tabPosition != "top";
54291         this.autoScroll = c.autoScroll || false;
54292         if(this.autoScroll){
54293             this.bodyEl.setStyle("overflow", "auto");
54294         }else{
54295             this.bodyEl.setStyle("overflow", "hidden");
54296         }
54297         //if(c.titlebar !== false){
54298             if((!c.titlebar && !c.title) || c.titlebar === false){
54299                 this.titleEl.hide();
54300             }else{
54301                 this.titleEl.show();
54302                 if(c.title){
54303                     this.titleTextEl.innerHTML = c.title;
54304                 }
54305             }
54306         //}
54307         this.duration = c.duration || .30;
54308         this.slideDuration = c.slideDuration || .45;
54309         this.config = c;
54310         if(c.collapsed){
54311             this.collapse(true);
54312         }
54313         if(c.hidden){
54314             this.hide();
54315         }
54316     },
54317     /**
54318      * Returns true if this region is currently visible.
54319      * @return {Boolean}
54320      */
54321     isVisible : function(){
54322         return this.visible;
54323     },
54324
54325     /**
54326      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54327      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54328      */
54329     setCollapsedTitle : function(title){
54330         title = title || "&#160;";
54331         if(this.collapsedTitleTextEl){
54332             this.collapsedTitleTextEl.innerHTML = title;
54333         }
54334     },
54335
54336     getBox : function(){
54337         var b;
54338         if(!this.collapsed){
54339             b = this.el.getBox(false, true);
54340         }else{
54341             b = this.collapsedEl.getBox(false, true);
54342         }
54343         return b;
54344     },
54345
54346     getMargins : function(){
54347         return this.collapsed ? this.cmargins : this.margins;
54348     },
54349
54350     highlight : function(){
54351         this.el.addClass("x-layout-panel-dragover");
54352     },
54353
54354     unhighlight : function(){
54355         this.el.removeClass("x-layout-panel-dragover");
54356     },
54357
54358     updateBox : function(box){
54359         this.box = box;
54360         if(!this.collapsed){
54361             this.el.dom.style.left = box.x + "px";
54362             this.el.dom.style.top = box.y + "px";
54363             this.updateBody(box.width, box.height);
54364         }else{
54365             this.collapsedEl.dom.style.left = box.x + "px";
54366             this.collapsedEl.dom.style.top = box.y + "px";
54367             this.collapsedEl.setSize(box.width, box.height);
54368         }
54369         if(this.tabs){
54370             this.tabs.autoSizeTabs();
54371         }
54372     },
54373
54374     updateBody : function(w, h){
54375         if(w !== null){
54376             this.el.setWidth(w);
54377             w -= this.el.getBorderWidth("rl");
54378             if(this.config.adjustments){
54379                 w += this.config.adjustments[0];
54380             }
54381         }
54382         if(h !== null){
54383             this.el.setHeight(h);
54384             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
54385             h -= this.el.getBorderWidth("tb");
54386             if(this.config.adjustments){
54387                 h += this.config.adjustments[1];
54388             }
54389             this.bodyEl.setHeight(h);
54390             if(this.tabs){
54391                 h = this.tabs.syncHeight(h);
54392             }
54393         }
54394         if(this.panelSize){
54395             w = w !== null ? w : this.panelSize.width;
54396             h = h !== null ? h : this.panelSize.height;
54397         }
54398         if(this.activePanel){
54399             var el = this.activePanel.getEl();
54400             w = w !== null ? w : el.getWidth();
54401             h = h !== null ? h : el.getHeight();
54402             this.panelSize = {width: w, height: h};
54403             this.activePanel.setSize(w, h);
54404         }
54405         if(Roo.isIE && this.tabs){
54406             this.tabs.el.repaint();
54407         }
54408     },
54409
54410     /**
54411      * Returns the container element for this region.
54412      * @return {Roo.Element}
54413      */
54414     getEl : function(){
54415         return this.el;
54416     },
54417
54418     /**
54419      * Hides this region.
54420      */
54421     hide : function(){
54422         if(!this.collapsed){
54423             this.el.dom.style.left = "-2000px";
54424             this.el.hide();
54425         }else{
54426             this.collapsedEl.dom.style.left = "-2000px";
54427             this.collapsedEl.hide();
54428         }
54429         this.visible = false;
54430         this.fireEvent("visibilitychange", this, false);
54431     },
54432
54433     /**
54434      * Shows this region if it was previously hidden.
54435      */
54436     show : function(){
54437         if(!this.collapsed){
54438             this.el.show();
54439         }else{
54440             this.collapsedEl.show();
54441         }
54442         this.visible = true;
54443         this.fireEvent("visibilitychange", this, true);
54444     },
54445
54446     closeClicked : function(){
54447         if(this.activePanel){
54448             this.remove(this.activePanel);
54449         }
54450     },
54451
54452     collapseClick : function(e){
54453         if(this.isSlid){
54454            e.stopPropagation();
54455            this.slideIn();
54456         }else{
54457            e.stopPropagation();
54458            this.slideOut();
54459         }
54460     },
54461
54462     /**
54463      * Collapses this region.
54464      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
54465      */
54466     collapse : function(skipAnim, skipCheck){
54467         if(this.collapsed) {
54468             return;
54469         }
54470         
54471         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
54472             
54473             this.collapsed = true;
54474             if(this.split){
54475                 this.split.el.hide();
54476             }
54477             if(this.config.animate && skipAnim !== true){
54478                 this.fireEvent("invalidated", this);
54479                 this.animateCollapse();
54480             }else{
54481                 this.el.setLocation(-20000,-20000);
54482                 this.el.hide();
54483                 this.collapsedEl.show();
54484                 this.fireEvent("collapsed", this);
54485                 this.fireEvent("invalidated", this);
54486             }
54487         }
54488         
54489     },
54490
54491     animateCollapse : function(){
54492         // overridden
54493     },
54494
54495     /**
54496      * Expands this region if it was previously collapsed.
54497      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
54498      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
54499      */
54500     expand : function(e, skipAnim){
54501         if(e) {
54502             e.stopPropagation();
54503         }
54504         if(!this.collapsed || this.el.hasActiveFx()) {
54505             return;
54506         }
54507         if(this.isSlid){
54508             this.afterSlideIn();
54509             skipAnim = true;
54510         }
54511         this.collapsed = false;
54512         if(this.config.animate && skipAnim !== true){
54513             this.animateExpand();
54514         }else{
54515             this.el.show();
54516             if(this.split){
54517                 this.split.el.show();
54518             }
54519             this.collapsedEl.setLocation(-2000,-2000);
54520             this.collapsedEl.hide();
54521             this.fireEvent("invalidated", this);
54522             this.fireEvent("expanded", this);
54523         }
54524     },
54525
54526     animateExpand : function(){
54527         // overridden
54528     },
54529
54530     initTabs : function()
54531     {
54532         this.bodyEl.setStyle("overflow", "hidden");
54533         var ts = new Roo.TabPanel(
54534                 this.bodyEl.dom,
54535                 {
54536                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
54537                     disableTooltips: this.config.disableTabTips,
54538                     toolbar : this.config.toolbar
54539                 }
54540         );
54541         if(this.config.hideTabs){
54542             ts.stripWrap.setDisplayed(false);
54543         }
54544         this.tabs = ts;
54545         ts.resizeTabs = this.config.resizeTabs === true;
54546         ts.minTabWidth = this.config.minTabWidth || 40;
54547         ts.maxTabWidth = this.config.maxTabWidth || 250;
54548         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
54549         ts.monitorResize = false;
54550         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54551         ts.bodyEl.addClass('x-layout-tabs-body');
54552         this.panels.each(this.initPanelAsTab, this);
54553     },
54554
54555     initPanelAsTab : function(panel){
54556         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
54557                     this.config.closeOnTab && panel.isClosable());
54558         if(panel.tabTip !== undefined){
54559             ti.setTooltip(panel.tabTip);
54560         }
54561         ti.on("activate", function(){
54562               this.setActivePanel(panel);
54563         }, this);
54564         if(this.config.closeOnTab){
54565             ti.on("beforeclose", function(t, e){
54566                 e.cancel = true;
54567                 this.remove(panel);
54568             }, this);
54569         }
54570         return ti;
54571     },
54572
54573     updatePanelTitle : function(panel, title){
54574         if(this.activePanel == panel){
54575             this.updateTitle(title);
54576         }
54577         if(this.tabs){
54578             var ti = this.tabs.getTab(panel.getEl().id);
54579             ti.setText(title);
54580             if(panel.tabTip !== undefined){
54581                 ti.setTooltip(panel.tabTip);
54582             }
54583         }
54584     },
54585
54586     updateTitle : function(title){
54587         if(this.titleTextEl && !this.config.title){
54588             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
54589         }
54590     },
54591
54592     setActivePanel : function(panel){
54593         panel = this.getPanel(panel);
54594         if(this.activePanel && this.activePanel != panel){
54595             this.activePanel.setActiveState(false);
54596         }
54597         this.activePanel = panel;
54598         panel.setActiveState(true);
54599         if(this.panelSize){
54600             panel.setSize(this.panelSize.width, this.panelSize.height);
54601         }
54602         if(this.closeBtn){
54603             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
54604         }
54605         this.updateTitle(panel.getTitle());
54606         if(this.tabs){
54607             this.fireEvent("invalidated", this);
54608         }
54609         this.fireEvent("panelactivated", this, panel);
54610     },
54611
54612     /**
54613      * Shows the specified panel.
54614      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
54615      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
54616      */
54617     showPanel : function(panel)
54618     {
54619         panel = this.getPanel(panel);
54620         if(panel){
54621             if(this.tabs){
54622                 var tab = this.tabs.getTab(panel.getEl().id);
54623                 if(tab.isHidden()){
54624                     this.tabs.unhideTab(tab.id);
54625                 }
54626                 tab.activate();
54627             }else{
54628                 this.setActivePanel(panel);
54629             }
54630         }
54631         return panel;
54632     },
54633
54634     /**
54635      * Get the active panel for this region.
54636      * @return {Roo.ContentPanel} The active panel or null
54637      */
54638     getActivePanel : function(){
54639         return this.activePanel;
54640     },
54641
54642     validateVisibility : function(){
54643         if(this.panels.getCount() < 1){
54644             this.updateTitle("&#160;");
54645             this.closeBtn.hide();
54646             this.hide();
54647         }else{
54648             if(!this.isVisible()){
54649                 this.show();
54650             }
54651         }
54652     },
54653
54654     /**
54655      * Adds the passed ContentPanel(s) to this region.
54656      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54657      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
54658      */
54659     add : function(panel){
54660         if(arguments.length > 1){
54661             for(var i = 0, len = arguments.length; i < len; i++) {
54662                 this.add(arguments[i]);
54663             }
54664             return null;
54665         }
54666         if(this.hasPanel(panel)){
54667             this.showPanel(panel);
54668             return panel;
54669         }
54670         panel.setRegion(this);
54671         this.panels.add(panel);
54672         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
54673             this.bodyEl.dom.appendChild(panel.getEl().dom);
54674             if(panel.background !== true){
54675                 this.setActivePanel(panel);
54676             }
54677             this.fireEvent("paneladded", this, panel);
54678             return panel;
54679         }
54680         if(!this.tabs){
54681             this.initTabs();
54682         }else{
54683             this.initPanelAsTab(panel);
54684         }
54685         if(panel.background !== true){
54686             this.tabs.activate(panel.getEl().id);
54687         }
54688         this.fireEvent("paneladded", this, panel);
54689         return panel;
54690     },
54691
54692     /**
54693      * Hides the tab for the specified panel.
54694      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54695      */
54696     hidePanel : function(panel){
54697         if(this.tabs && (panel = this.getPanel(panel))){
54698             this.tabs.hideTab(panel.getEl().id);
54699         }
54700     },
54701
54702     /**
54703      * Unhides the tab for a previously hidden panel.
54704      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54705      */
54706     unhidePanel : function(panel){
54707         if(this.tabs && (panel = this.getPanel(panel))){
54708             this.tabs.unhideTab(panel.getEl().id);
54709         }
54710     },
54711
54712     clearPanels : function(){
54713         while(this.panels.getCount() > 0){
54714              this.remove(this.panels.first());
54715         }
54716     },
54717
54718     /**
54719      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54720      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54721      * @param {Boolean} preservePanel Overrides the config preservePanel option
54722      * @return {Roo.ContentPanel} The panel that was removed
54723      */
54724     remove : function(panel, preservePanel){
54725         panel = this.getPanel(panel);
54726         if(!panel){
54727             return null;
54728         }
54729         var e = {};
54730         this.fireEvent("beforeremove", this, panel, e);
54731         if(e.cancel === true){
54732             return null;
54733         }
54734         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
54735         var panelId = panel.getId();
54736         this.panels.removeKey(panelId);
54737         if(preservePanel){
54738             document.body.appendChild(panel.getEl().dom);
54739         }
54740         if(this.tabs){
54741             this.tabs.removeTab(panel.getEl().id);
54742         }else if (!preservePanel){
54743             this.bodyEl.dom.removeChild(panel.getEl().dom);
54744         }
54745         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
54746             var p = this.panels.first();
54747             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
54748             tempEl.appendChild(p.getEl().dom);
54749             this.bodyEl.update("");
54750             this.bodyEl.dom.appendChild(p.getEl().dom);
54751             tempEl = null;
54752             this.updateTitle(p.getTitle());
54753             this.tabs = null;
54754             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54755             this.setActivePanel(p);
54756         }
54757         panel.setRegion(null);
54758         if(this.activePanel == panel){
54759             this.activePanel = null;
54760         }
54761         if(this.config.autoDestroy !== false && preservePanel !== true){
54762             try{panel.destroy();}catch(e){}
54763         }
54764         this.fireEvent("panelremoved", this, panel);
54765         return panel;
54766     },
54767
54768     /**
54769      * Returns the TabPanel component used by this region
54770      * @return {Roo.TabPanel}
54771      */
54772     getTabs : function(){
54773         return this.tabs;
54774     },
54775
54776     createTool : function(parentEl, className){
54777         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
54778             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
54779         btn.addClassOnOver("x-layout-tools-button-over");
54780         return btn;
54781     }
54782 });/*
54783  * Based on:
54784  * Ext JS Library 1.1.1
54785  * Copyright(c) 2006-2007, Ext JS, LLC.
54786  *
54787  * Originally Released Under LGPL - original licence link has changed is not relivant.
54788  *
54789  * Fork - LGPL
54790  * <script type="text/javascript">
54791  */
54792  
54793
54794
54795 /**
54796  * @class Roo.SplitLayoutRegion
54797  * @extends Roo.LayoutRegion
54798  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
54799  */
54800 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
54801     this.cursor = cursor;
54802     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
54803 };
54804
54805 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
54806     splitTip : "Drag to resize.",
54807     collapsibleSplitTip : "Drag to resize. Double click to hide.",
54808     useSplitTips : false,
54809
54810     applyConfig : function(config){
54811         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
54812         if(config.split){
54813             if(!this.split){
54814                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
54815                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
54816                 /** The SplitBar for this region 
54817                 * @type Roo.SplitBar */
54818                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
54819                 this.split.on("moved", this.onSplitMove, this);
54820                 this.split.useShim = config.useShim === true;
54821                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
54822                 if(this.useSplitTips){
54823                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
54824                 }
54825                 if(config.collapsible){
54826                     this.split.el.on("dblclick", this.collapse,  this);
54827                 }
54828             }
54829             if(typeof config.minSize != "undefined"){
54830                 this.split.minSize = config.minSize;
54831             }
54832             if(typeof config.maxSize != "undefined"){
54833                 this.split.maxSize = config.maxSize;
54834             }
54835             if(config.hideWhenEmpty || config.hidden || config.collapsed){
54836                 this.hideSplitter();
54837             }
54838         }
54839     },
54840
54841     getHMaxSize : function(){
54842          var cmax = this.config.maxSize || 10000;
54843          var center = this.mgr.getRegion("center");
54844          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
54845     },
54846
54847     getVMaxSize : function(){
54848          var cmax = this.config.maxSize || 10000;
54849          var center = this.mgr.getRegion("center");
54850          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
54851     },
54852
54853     onSplitMove : function(split, newSize){
54854         this.fireEvent("resized", this, newSize);
54855     },
54856     
54857     /** 
54858      * Returns the {@link Roo.SplitBar} for this region.
54859      * @return {Roo.SplitBar}
54860      */
54861     getSplitBar : function(){
54862         return this.split;
54863     },
54864     
54865     hide : function(){
54866         this.hideSplitter();
54867         Roo.SplitLayoutRegion.superclass.hide.call(this);
54868     },
54869
54870     hideSplitter : function(){
54871         if(this.split){
54872             this.split.el.setLocation(-2000,-2000);
54873             this.split.el.hide();
54874         }
54875     },
54876
54877     show : function(){
54878         if(this.split){
54879             this.split.el.show();
54880         }
54881         Roo.SplitLayoutRegion.superclass.show.call(this);
54882     },
54883     
54884     beforeSlide: function(){
54885         if(Roo.isGecko){// firefox overflow auto bug workaround
54886             this.bodyEl.clip();
54887             if(this.tabs) {
54888                 this.tabs.bodyEl.clip();
54889             }
54890             if(this.activePanel){
54891                 this.activePanel.getEl().clip();
54892                 
54893                 if(this.activePanel.beforeSlide){
54894                     this.activePanel.beforeSlide();
54895                 }
54896             }
54897         }
54898     },
54899     
54900     afterSlide : function(){
54901         if(Roo.isGecko){// firefox overflow auto bug workaround
54902             this.bodyEl.unclip();
54903             if(this.tabs) {
54904                 this.tabs.bodyEl.unclip();
54905             }
54906             if(this.activePanel){
54907                 this.activePanel.getEl().unclip();
54908                 if(this.activePanel.afterSlide){
54909                     this.activePanel.afterSlide();
54910                 }
54911             }
54912         }
54913     },
54914
54915     initAutoHide : function(){
54916         if(this.autoHide !== false){
54917             if(!this.autoHideHd){
54918                 var st = new Roo.util.DelayedTask(this.slideIn, this);
54919                 this.autoHideHd = {
54920                     "mouseout": function(e){
54921                         if(!e.within(this.el, true)){
54922                             st.delay(500);
54923                         }
54924                     },
54925                     "mouseover" : function(e){
54926                         st.cancel();
54927                     },
54928                     scope : this
54929                 };
54930             }
54931             this.el.on(this.autoHideHd);
54932         }
54933     },
54934
54935     clearAutoHide : function(){
54936         if(this.autoHide !== false){
54937             this.el.un("mouseout", this.autoHideHd.mouseout);
54938             this.el.un("mouseover", this.autoHideHd.mouseover);
54939         }
54940     },
54941
54942     clearMonitor : function(){
54943         Roo.get(document).un("click", this.slideInIf, this);
54944     },
54945
54946     // these names are backwards but not changed for compat
54947     slideOut : function(){
54948         if(this.isSlid || this.el.hasActiveFx()){
54949             return;
54950         }
54951         this.isSlid = true;
54952         if(this.collapseBtn){
54953             this.collapseBtn.hide();
54954         }
54955         this.closeBtnState = this.closeBtn.getStyle('display');
54956         this.closeBtn.hide();
54957         if(this.stickBtn){
54958             this.stickBtn.show();
54959         }
54960         this.el.show();
54961         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
54962         this.beforeSlide();
54963         this.el.setStyle("z-index", 10001);
54964         this.el.slideIn(this.getSlideAnchor(), {
54965             callback: function(){
54966                 this.afterSlide();
54967                 this.initAutoHide();
54968                 Roo.get(document).on("click", this.slideInIf, this);
54969                 this.fireEvent("slideshow", this);
54970             },
54971             scope: this,
54972             block: true
54973         });
54974     },
54975
54976     afterSlideIn : function(){
54977         this.clearAutoHide();
54978         this.isSlid = false;
54979         this.clearMonitor();
54980         this.el.setStyle("z-index", "");
54981         if(this.collapseBtn){
54982             this.collapseBtn.show();
54983         }
54984         this.closeBtn.setStyle('display', this.closeBtnState);
54985         if(this.stickBtn){
54986             this.stickBtn.hide();
54987         }
54988         this.fireEvent("slidehide", this);
54989     },
54990
54991     slideIn : function(cb){
54992         if(!this.isSlid || this.el.hasActiveFx()){
54993             Roo.callback(cb);
54994             return;
54995         }
54996         this.isSlid = false;
54997         this.beforeSlide();
54998         this.el.slideOut(this.getSlideAnchor(), {
54999             callback: function(){
55000                 this.el.setLeftTop(-10000, -10000);
55001                 this.afterSlide();
55002                 this.afterSlideIn();
55003                 Roo.callback(cb);
55004             },
55005             scope: this,
55006             block: true
55007         });
55008     },
55009     
55010     slideInIf : function(e){
55011         if(!e.within(this.el)){
55012             this.slideIn();
55013         }
55014     },
55015
55016     animateCollapse : function(){
55017         this.beforeSlide();
55018         this.el.setStyle("z-index", 20000);
55019         var anchor = this.getSlideAnchor();
55020         this.el.slideOut(anchor, {
55021             callback : function(){
55022                 this.el.setStyle("z-index", "");
55023                 this.collapsedEl.slideIn(anchor, {duration:.3});
55024                 this.afterSlide();
55025                 this.el.setLocation(-10000,-10000);
55026                 this.el.hide();
55027                 this.fireEvent("collapsed", this);
55028             },
55029             scope: this,
55030             block: true
55031         });
55032     },
55033
55034     animateExpand : function(){
55035         this.beforeSlide();
55036         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
55037         this.el.setStyle("z-index", 20000);
55038         this.collapsedEl.hide({
55039             duration:.1
55040         });
55041         this.el.slideIn(this.getSlideAnchor(), {
55042             callback : function(){
55043                 this.el.setStyle("z-index", "");
55044                 this.afterSlide();
55045                 if(this.split){
55046                     this.split.el.show();
55047                 }
55048                 this.fireEvent("invalidated", this);
55049                 this.fireEvent("expanded", this);
55050             },
55051             scope: this,
55052             block: true
55053         });
55054     },
55055
55056     anchors : {
55057         "west" : "left",
55058         "east" : "right",
55059         "north" : "top",
55060         "south" : "bottom"
55061     },
55062
55063     sanchors : {
55064         "west" : "l",
55065         "east" : "r",
55066         "north" : "t",
55067         "south" : "b"
55068     },
55069
55070     canchors : {
55071         "west" : "tl-tr",
55072         "east" : "tr-tl",
55073         "north" : "tl-bl",
55074         "south" : "bl-tl"
55075     },
55076
55077     getAnchor : function(){
55078         return this.anchors[this.position];
55079     },
55080
55081     getCollapseAnchor : function(){
55082         return this.canchors[this.position];
55083     },
55084
55085     getSlideAnchor : function(){
55086         return this.sanchors[this.position];
55087     },
55088
55089     getAlignAdj : function(){
55090         var cm = this.cmargins;
55091         switch(this.position){
55092             case "west":
55093                 return [0, 0];
55094             break;
55095             case "east":
55096                 return [0, 0];
55097             break;
55098             case "north":
55099                 return [0, 0];
55100             break;
55101             case "south":
55102                 return [0, 0];
55103             break;
55104         }
55105     },
55106
55107     getExpandAdj : function(){
55108         var c = this.collapsedEl, cm = this.cmargins;
55109         switch(this.position){
55110             case "west":
55111                 return [-(cm.right+c.getWidth()+cm.left), 0];
55112             break;
55113             case "east":
55114                 return [cm.right+c.getWidth()+cm.left, 0];
55115             break;
55116             case "north":
55117                 return [0, -(cm.top+cm.bottom+c.getHeight())];
55118             break;
55119             case "south":
55120                 return [0, cm.top+cm.bottom+c.getHeight()];
55121             break;
55122         }
55123     }
55124 });/*
55125  * Based on:
55126  * Ext JS Library 1.1.1
55127  * Copyright(c) 2006-2007, Ext JS, LLC.
55128  *
55129  * Originally Released Under LGPL - original licence link has changed is not relivant.
55130  *
55131  * Fork - LGPL
55132  * <script type="text/javascript">
55133  */
55134 /*
55135  * These classes are private internal classes
55136  */
55137 Roo.CenterLayoutRegion = function(mgr, config){
55138     Roo.LayoutRegion.call(this, mgr, config, "center");
55139     this.visible = true;
55140     this.minWidth = config.minWidth || 20;
55141     this.minHeight = config.minHeight || 20;
55142 };
55143
55144 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
55145     hide : function(){
55146         // center panel can't be hidden
55147     },
55148     
55149     show : function(){
55150         // center panel can't be hidden
55151     },
55152     
55153     getMinWidth: function(){
55154         return this.minWidth;
55155     },
55156     
55157     getMinHeight: function(){
55158         return this.minHeight;
55159     }
55160 });
55161
55162
55163 Roo.NorthLayoutRegion = function(mgr, config){
55164     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
55165     if(this.split){
55166         this.split.placement = Roo.SplitBar.TOP;
55167         this.split.orientation = Roo.SplitBar.VERTICAL;
55168         this.split.el.addClass("x-layout-split-v");
55169     }
55170     var size = config.initialSize || config.height;
55171     if(typeof size != "undefined"){
55172         this.el.setHeight(size);
55173     }
55174 };
55175 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
55176     orientation: Roo.SplitBar.VERTICAL,
55177     getBox : function(){
55178         if(this.collapsed){
55179             return this.collapsedEl.getBox();
55180         }
55181         var box = this.el.getBox();
55182         if(this.split){
55183             box.height += this.split.el.getHeight();
55184         }
55185         return box;
55186     },
55187     
55188     updateBox : function(box){
55189         if(this.split && !this.collapsed){
55190             box.height -= this.split.el.getHeight();
55191             this.split.el.setLeft(box.x);
55192             this.split.el.setTop(box.y+box.height);
55193             this.split.el.setWidth(box.width);
55194         }
55195         if(this.collapsed){
55196             this.updateBody(box.width, null);
55197         }
55198         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55199     }
55200 });
55201
55202 Roo.SouthLayoutRegion = function(mgr, config){
55203     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
55204     if(this.split){
55205         this.split.placement = Roo.SplitBar.BOTTOM;
55206         this.split.orientation = Roo.SplitBar.VERTICAL;
55207         this.split.el.addClass("x-layout-split-v");
55208     }
55209     var size = config.initialSize || config.height;
55210     if(typeof size != "undefined"){
55211         this.el.setHeight(size);
55212     }
55213 };
55214 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
55215     orientation: Roo.SplitBar.VERTICAL,
55216     getBox : function(){
55217         if(this.collapsed){
55218             return this.collapsedEl.getBox();
55219         }
55220         var box = this.el.getBox();
55221         if(this.split){
55222             var sh = this.split.el.getHeight();
55223             box.height += sh;
55224             box.y -= sh;
55225         }
55226         return box;
55227     },
55228     
55229     updateBox : function(box){
55230         if(this.split && !this.collapsed){
55231             var sh = this.split.el.getHeight();
55232             box.height -= sh;
55233             box.y += sh;
55234             this.split.el.setLeft(box.x);
55235             this.split.el.setTop(box.y-sh);
55236             this.split.el.setWidth(box.width);
55237         }
55238         if(this.collapsed){
55239             this.updateBody(box.width, null);
55240         }
55241         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55242     }
55243 });
55244
55245 Roo.EastLayoutRegion = function(mgr, config){
55246     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
55247     if(this.split){
55248         this.split.placement = Roo.SplitBar.RIGHT;
55249         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55250         this.split.el.addClass("x-layout-split-h");
55251     }
55252     var size = config.initialSize || config.width;
55253     if(typeof size != "undefined"){
55254         this.el.setWidth(size);
55255     }
55256 };
55257 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
55258     orientation: Roo.SplitBar.HORIZONTAL,
55259     getBox : function(){
55260         if(this.collapsed){
55261             return this.collapsedEl.getBox();
55262         }
55263         var box = this.el.getBox();
55264         if(this.split){
55265             var sw = this.split.el.getWidth();
55266             box.width += sw;
55267             box.x -= sw;
55268         }
55269         return box;
55270     },
55271
55272     updateBox : function(box){
55273         if(this.split && !this.collapsed){
55274             var sw = this.split.el.getWidth();
55275             box.width -= sw;
55276             this.split.el.setLeft(box.x);
55277             this.split.el.setTop(box.y);
55278             this.split.el.setHeight(box.height);
55279             box.x += sw;
55280         }
55281         if(this.collapsed){
55282             this.updateBody(null, box.height);
55283         }
55284         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55285     }
55286 });
55287
55288 Roo.WestLayoutRegion = function(mgr, config){
55289     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55290     if(this.split){
55291         this.split.placement = Roo.SplitBar.LEFT;
55292         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55293         this.split.el.addClass("x-layout-split-h");
55294     }
55295     var size = config.initialSize || config.width;
55296     if(typeof size != "undefined"){
55297         this.el.setWidth(size);
55298     }
55299 };
55300 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55301     orientation: Roo.SplitBar.HORIZONTAL,
55302     getBox : function(){
55303         if(this.collapsed){
55304             return this.collapsedEl.getBox();
55305         }
55306         var box = this.el.getBox();
55307         if(this.split){
55308             box.width += this.split.el.getWidth();
55309         }
55310         return box;
55311     },
55312     
55313     updateBox : function(box){
55314         if(this.split && !this.collapsed){
55315             var sw = this.split.el.getWidth();
55316             box.width -= sw;
55317             this.split.el.setLeft(box.x+box.width);
55318             this.split.el.setTop(box.y);
55319             this.split.el.setHeight(box.height);
55320         }
55321         if(this.collapsed){
55322             this.updateBody(null, box.height);
55323         }
55324         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55325     }
55326 });
55327 /*
55328  * Based on:
55329  * Ext JS Library 1.1.1
55330  * Copyright(c) 2006-2007, Ext JS, LLC.
55331  *
55332  * Originally Released Under LGPL - original licence link has changed is not relivant.
55333  *
55334  * Fork - LGPL
55335  * <script type="text/javascript">
55336  */
55337  
55338  
55339 /*
55340  * Private internal class for reading and applying state
55341  */
55342 Roo.LayoutStateManager = function(layout){
55343      // default empty state
55344      this.state = {
55345         north: {},
55346         south: {},
55347         east: {},
55348         west: {}       
55349     };
55350 };
55351
55352 Roo.LayoutStateManager.prototype = {
55353     init : function(layout, provider){
55354         this.provider = provider;
55355         var state = provider.get(layout.id+"-layout-state");
55356         if(state){
55357             var wasUpdating = layout.isUpdating();
55358             if(!wasUpdating){
55359                 layout.beginUpdate();
55360             }
55361             for(var key in state){
55362                 if(typeof state[key] != "function"){
55363                     var rstate = state[key];
55364                     var r = layout.getRegion(key);
55365                     if(r && rstate){
55366                         if(rstate.size){
55367                             r.resizeTo(rstate.size);
55368                         }
55369                         if(rstate.collapsed == true){
55370                             r.collapse(true);
55371                         }else{
55372                             r.expand(null, true);
55373                         }
55374                     }
55375                 }
55376             }
55377             if(!wasUpdating){
55378                 layout.endUpdate();
55379             }
55380             this.state = state; 
55381         }
55382         this.layout = layout;
55383         layout.on("regionresized", this.onRegionResized, this);
55384         layout.on("regioncollapsed", this.onRegionCollapsed, this);
55385         layout.on("regionexpanded", this.onRegionExpanded, this);
55386     },
55387     
55388     storeState : function(){
55389         this.provider.set(this.layout.id+"-layout-state", this.state);
55390     },
55391     
55392     onRegionResized : function(region, newSize){
55393         this.state[region.getPosition()].size = newSize;
55394         this.storeState();
55395     },
55396     
55397     onRegionCollapsed : function(region){
55398         this.state[region.getPosition()].collapsed = true;
55399         this.storeState();
55400     },
55401     
55402     onRegionExpanded : function(region){
55403         this.state[region.getPosition()].collapsed = false;
55404         this.storeState();
55405     }
55406 };/*
55407  * Based on:
55408  * Ext JS Library 1.1.1
55409  * Copyright(c) 2006-2007, Ext JS, LLC.
55410  *
55411  * Originally Released Under LGPL - original licence link has changed is not relivant.
55412  *
55413  * Fork - LGPL
55414  * <script type="text/javascript">
55415  */
55416 /**
55417  * @class Roo.ContentPanel
55418  * @extends Roo.util.Observable
55419  * @children Roo.form.Form Roo.JsonView Roo.View
55420  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
55421  * A basic ContentPanel element.
55422  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
55423  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
55424  * @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
55425  * @cfg {Boolean}   closable      True if the panel can be closed/removed
55426  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
55427  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
55428  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
55429  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
55430  * @cfg {String} title          The title for this panel
55431  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
55432  * @cfg {String} url            Calls {@link #setUrl} with this value
55433  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
55434  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
55435  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
55436  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
55437  * @cfg {String}    style  Extra style to add to the content panel
55438  * @cfg {Roo.menu.Menu} menu  popup menu
55439
55440  * @constructor
55441  * Create a new ContentPanel.
55442  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
55443  * @param {String/Object} config A string to set only the title or a config object
55444  * @param {String} content (optional) Set the HTML content for this panel
55445  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
55446  */
55447 Roo.ContentPanel = function(el, config, content){
55448     
55449      
55450     /*
55451     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
55452         config = el;
55453         el = Roo.id();
55454     }
55455     if (config && config.parentLayout) { 
55456         el = config.parentLayout.el.createChild(); 
55457     }
55458     */
55459     if(el.autoCreate){ // xtype is available if this is called from factory
55460         config = el;
55461         el = Roo.id();
55462     }
55463     this.el = Roo.get(el);
55464     if(!this.el && config && config.autoCreate){
55465         if(typeof config.autoCreate == "object"){
55466             if(!config.autoCreate.id){
55467                 config.autoCreate.id = config.id||el;
55468             }
55469             this.el = Roo.DomHelper.append(document.body,
55470                         config.autoCreate, true);
55471         }else{
55472             this.el = Roo.DomHelper.append(document.body,
55473                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
55474         }
55475     }
55476     
55477     
55478     this.closable = false;
55479     this.loaded = false;
55480     this.active = false;
55481     if(typeof config == "string"){
55482         this.title = config;
55483     }else{
55484         Roo.apply(this, config);
55485     }
55486     
55487     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
55488         this.wrapEl = this.el.wrap();
55489         this.toolbar.container = this.el.insertSibling(false, 'before');
55490         this.toolbar = new Roo.Toolbar(this.toolbar);
55491     }
55492     
55493     // xtype created footer. - not sure if will work as we normally have to render first..
55494     if (this.footer && !this.footer.el && this.footer.xtype) {
55495         if (!this.wrapEl) {
55496             this.wrapEl = this.el.wrap();
55497         }
55498     
55499         this.footer.container = this.wrapEl.createChild();
55500          
55501         this.footer = Roo.factory(this.footer, Roo);
55502         
55503     }
55504     
55505     if(this.resizeEl){
55506         this.resizeEl = Roo.get(this.resizeEl, true);
55507     }else{
55508         this.resizeEl = this.el;
55509     }
55510     // handle view.xtype
55511     
55512  
55513     
55514     
55515     this.addEvents({
55516         /**
55517          * @event activate
55518          * Fires when this panel is activated. 
55519          * @param {Roo.ContentPanel} this
55520          */
55521         "activate" : true,
55522         /**
55523          * @event deactivate
55524          * Fires when this panel is activated. 
55525          * @param {Roo.ContentPanel} this
55526          */
55527         "deactivate" : true,
55528
55529         /**
55530          * @event resize
55531          * Fires when this panel is resized if fitToFrame is true.
55532          * @param {Roo.ContentPanel} this
55533          * @param {Number} width The width after any component adjustments
55534          * @param {Number} height The height after any component adjustments
55535          */
55536         "resize" : true,
55537         
55538          /**
55539          * @event render
55540          * Fires when this tab is created
55541          * @param {Roo.ContentPanel} this
55542          */
55543         "render" : true
55544          
55545         
55546     });
55547     
55548
55549     
55550     
55551     if(this.autoScroll){
55552         this.resizeEl.setStyle("overflow", "auto");
55553     } else {
55554         // fix randome scrolling
55555         this.el.on('scroll', function() {
55556             Roo.log('fix random scolling');
55557             this.scrollTo('top',0); 
55558         });
55559     }
55560     content = content || this.content;
55561     if(content){
55562         this.setContent(content);
55563     }
55564     if(config && config.url){
55565         this.setUrl(this.url, this.params, this.loadOnce);
55566     }
55567     
55568     
55569     
55570     Roo.ContentPanel.superclass.constructor.call(this);
55571     
55572     if (this.view && typeof(this.view.xtype) != 'undefined') {
55573         this.view.el = this.el.appendChild(document.createElement("div"));
55574         this.view = Roo.factory(this.view); 
55575         this.view.render  &&  this.view.render(false, '');  
55576     }
55577     
55578     
55579     this.fireEvent('render', this);
55580 };
55581
55582 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
55583     tabTip:'',
55584     setRegion : function(region){
55585         this.region = region;
55586         if(region){
55587            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
55588         }else{
55589            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
55590         } 
55591     },
55592     
55593     /**
55594      * Returns the toolbar for this Panel if one was configured. 
55595      * @return {Roo.Toolbar} 
55596      */
55597     getToolbar : function(){
55598         return this.toolbar;
55599     },
55600     
55601     setActiveState : function(active){
55602         this.active = active;
55603         if(!active){
55604             this.fireEvent("deactivate", this);
55605         }else{
55606             this.fireEvent("activate", this);
55607         }
55608     },
55609     /**
55610      * Updates this panel's element
55611      * @param {String} content The new content
55612      * @param {Boolean} loadScripts (optional) true to look for and process scripts
55613     */
55614     setContent : function(content, loadScripts){
55615         this.el.update(content, loadScripts);
55616     },
55617
55618     ignoreResize : function(w, h){
55619         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
55620             return true;
55621         }else{
55622             this.lastSize = {width: w, height: h};
55623             return false;
55624         }
55625     },
55626     /**
55627      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
55628      * @return {Roo.UpdateManager} The UpdateManager
55629      */
55630     getUpdateManager : function(){
55631         return this.el.getUpdateManager();
55632     },
55633      /**
55634      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
55635      * @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:
55636 <pre><code>
55637 panel.load({
55638     url: "your-url.php",
55639     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
55640     callback: yourFunction,
55641     scope: yourObject, //(optional scope)
55642     discardUrl: false,
55643     nocache: false,
55644     text: "Loading...",
55645     timeout: 30,
55646     scripts: false
55647 });
55648 </code></pre>
55649      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
55650      * 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.
55651      * @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}
55652      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
55653      * @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.
55654      * @return {Roo.ContentPanel} this
55655      */
55656     load : function(){
55657         var um = this.el.getUpdateManager();
55658         um.update.apply(um, arguments);
55659         return this;
55660     },
55661
55662
55663     /**
55664      * 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.
55665      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
55666      * @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)
55667      * @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)
55668      * @return {Roo.UpdateManager} The UpdateManager
55669      */
55670     setUrl : function(url, params, loadOnce){
55671         if(this.refreshDelegate){
55672             this.removeListener("activate", this.refreshDelegate);
55673         }
55674         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
55675         this.on("activate", this.refreshDelegate);
55676         return this.el.getUpdateManager();
55677     },
55678     
55679     _handleRefresh : function(url, params, loadOnce){
55680         if(!loadOnce || !this.loaded){
55681             var updater = this.el.getUpdateManager();
55682             updater.update(url, params, this._setLoaded.createDelegate(this));
55683         }
55684     },
55685     
55686     _setLoaded : function(){
55687         this.loaded = true;
55688     }, 
55689     
55690     /**
55691      * Returns this panel's id
55692      * @return {String} 
55693      */
55694     getId : function(){
55695         return this.el.id;
55696     },
55697     
55698     /** 
55699      * Returns this panel's element - used by regiosn to add.
55700      * @return {Roo.Element} 
55701      */
55702     getEl : function(){
55703         return this.wrapEl || this.el;
55704     },
55705     
55706     adjustForComponents : function(width, height)
55707     {
55708         //Roo.log('adjustForComponents ');
55709         if(this.resizeEl != this.el){
55710             width -= this.el.getFrameWidth('lr');
55711             height -= this.el.getFrameWidth('tb');
55712         }
55713         if(this.toolbar){
55714             var te = this.toolbar.getEl();
55715             height -= te.getHeight();
55716             te.setWidth(width);
55717         }
55718         if(this.footer){
55719             var te = this.footer.getEl();
55720             //Roo.log("footer:" + te.getHeight());
55721             
55722             height -= te.getHeight();
55723             te.setWidth(width);
55724         }
55725         
55726         
55727         if(this.adjustments){
55728             width += this.adjustments[0];
55729             height += this.adjustments[1];
55730         }
55731         return {"width": width, "height": height};
55732     },
55733     
55734     setSize : function(width, height){
55735         if(this.fitToFrame && !this.ignoreResize(width, height)){
55736             if(this.fitContainer && this.resizeEl != this.el){
55737                 this.el.setSize(width, height);
55738             }
55739             var size = this.adjustForComponents(width, height);
55740             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
55741             this.fireEvent('resize', this, size.width, size.height);
55742         }
55743     },
55744     
55745     /**
55746      * Returns this panel's title
55747      * @return {String} 
55748      */
55749     getTitle : function(){
55750         return this.title;
55751     },
55752     
55753     /**
55754      * Set this panel's title
55755      * @param {String} title
55756      */
55757     setTitle : function(title){
55758         this.title = title;
55759         if(this.region){
55760             this.region.updatePanelTitle(this, title);
55761         }
55762     },
55763     
55764     /**
55765      * Returns true is this panel was configured to be closable
55766      * @return {Boolean} 
55767      */
55768     isClosable : function(){
55769         return this.closable;
55770     },
55771     
55772     beforeSlide : function(){
55773         this.el.clip();
55774         this.resizeEl.clip();
55775     },
55776     
55777     afterSlide : function(){
55778         this.el.unclip();
55779         this.resizeEl.unclip();
55780     },
55781     
55782     /**
55783      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
55784      *   Will fail silently if the {@link #setUrl} method has not been called.
55785      *   This does not activate the panel, just updates its content.
55786      */
55787     refresh : function(){
55788         if(this.refreshDelegate){
55789            this.loaded = false;
55790            this.refreshDelegate();
55791         }
55792     },
55793     
55794     /**
55795      * Destroys this panel
55796      */
55797     destroy : function(){
55798         this.el.removeAllListeners();
55799         var tempEl = document.createElement("span");
55800         tempEl.appendChild(this.el.dom);
55801         tempEl.innerHTML = "";
55802         this.el.remove();
55803         this.el = null;
55804     },
55805     
55806     /**
55807      * form - if the content panel contains a form - this is a reference to it.
55808      * @type {Roo.form.Form}
55809      */
55810     form : false,
55811     /**
55812      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
55813      *    This contains a reference to it.
55814      * @type {Roo.View}
55815      */
55816     view : false,
55817     
55818       /**
55819      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
55820      * <pre><code>
55821
55822 layout.addxtype({
55823        xtype : 'Form',
55824        items: [ .... ]
55825    }
55826 );
55827
55828 </code></pre>
55829      * @param {Object} cfg Xtype definition of item to add.
55830      */
55831     
55832     addxtype : function(cfg) {
55833         // add form..
55834         if (cfg.xtype.match(/^Form$/)) {
55835             
55836             var el;
55837             //if (this.footer) {
55838             //    el = this.footer.container.insertSibling(false, 'before');
55839             //} else {
55840                 el = this.el.createChild();
55841             //}
55842
55843             this.form = new  Roo.form.Form(cfg);
55844             
55845             
55846             if ( this.form.allItems.length) {
55847                 this.form.render(el.dom);
55848             }
55849             return this.form;
55850         }
55851         // should only have one of theses..
55852         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
55853             // views.. should not be just added - used named prop 'view''
55854             
55855             cfg.el = this.el.appendChild(document.createElement("div"));
55856             // factory?
55857             
55858             var ret = new Roo.factory(cfg);
55859              
55860              ret.render && ret.render(false, ''); // render blank..
55861             this.view = ret;
55862             return ret;
55863         }
55864         return false;
55865     }
55866 });
55867
55868 /**
55869  * @class Roo.GridPanel
55870  * @extends Roo.ContentPanel
55871  * @constructor
55872  * Create a new GridPanel.
55873  * @param {Roo.grid.Grid} grid The grid for this panel
55874  * @param {String/Object} config A string to set only the panel's title, or a config object
55875  */
55876 Roo.GridPanel = function(grid, config){
55877     
55878   
55879     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
55880         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
55881         
55882     this.wrapper.dom.appendChild(grid.getGridEl().dom);
55883     
55884     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
55885     
55886     if(this.toolbar){
55887         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
55888     }
55889     // xtype created footer. - not sure if will work as we normally have to render first..
55890     if (this.footer && !this.footer.el && this.footer.xtype) {
55891         
55892         this.footer.container = this.grid.getView().getFooterPanel(true);
55893         this.footer.dataSource = this.grid.dataSource;
55894         this.footer = Roo.factory(this.footer, Roo);
55895         
55896     }
55897     
55898     grid.monitorWindowResize = false; // turn off autosizing
55899     grid.autoHeight = false;
55900     grid.autoWidth = false;
55901     this.grid = grid;
55902     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
55903 };
55904
55905 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
55906     getId : function(){
55907         return this.grid.id;
55908     },
55909     
55910     /**
55911      * Returns the grid for this panel
55912      * @return {Roo.grid.Grid} 
55913      */
55914     getGrid : function(){
55915         return this.grid;    
55916     },
55917     
55918     setSize : function(width, height){
55919         if(!this.ignoreResize(width, height)){
55920             var grid = this.grid;
55921             var size = this.adjustForComponents(width, height);
55922             grid.getGridEl().setSize(size.width, size.height);
55923             grid.autoSize();
55924         }
55925     },
55926     
55927     beforeSlide : function(){
55928         this.grid.getView().scroller.clip();
55929     },
55930     
55931     afterSlide : function(){
55932         this.grid.getView().scroller.unclip();
55933     },
55934     
55935     destroy : function(){
55936         this.grid.destroy();
55937         delete this.grid;
55938         Roo.GridPanel.superclass.destroy.call(this); 
55939     }
55940 });
55941
55942
55943 /**
55944  * @class Roo.NestedLayoutPanel
55945  * @extends Roo.ContentPanel
55946  * @constructor
55947  * Create a new NestedLayoutPanel.
55948  * 
55949  * 
55950  * @param {Roo.BorderLayout} layout [required] The layout for this panel
55951  * @param {String/Object} config A string to set only the title or a config object
55952  */
55953 Roo.NestedLayoutPanel = function(layout, config)
55954 {
55955     // construct with only one argument..
55956     /* FIXME - implement nicer consturctors
55957     if (layout.layout) {
55958         config = layout;
55959         layout = config.layout;
55960         delete config.layout;
55961     }
55962     if (layout.xtype && !layout.getEl) {
55963         // then layout needs constructing..
55964         layout = Roo.factory(layout, Roo);
55965     }
55966     */
55967     
55968     
55969     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
55970     
55971     layout.monitorWindowResize = false; // turn off autosizing
55972     this.layout = layout;
55973     this.layout.getEl().addClass("x-layout-nested-layout");
55974     
55975     
55976     
55977     
55978 };
55979
55980 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
55981
55982     setSize : function(width, height){
55983         if(!this.ignoreResize(width, height)){
55984             var size = this.adjustForComponents(width, height);
55985             var el = this.layout.getEl();
55986             el.setSize(size.width, size.height);
55987             var touch = el.dom.offsetWidth;
55988             this.layout.layout();
55989             // ie requires a double layout on the first pass
55990             if(Roo.isIE && !this.initialized){
55991                 this.initialized = true;
55992                 this.layout.layout();
55993             }
55994         }
55995     },
55996     
55997     // activate all subpanels if not currently active..
55998     
55999     setActiveState : function(active){
56000         this.active = active;
56001         if(!active){
56002             this.fireEvent("deactivate", this);
56003             return;
56004         }
56005         
56006         this.fireEvent("activate", this);
56007         // not sure if this should happen before or after..
56008         if (!this.layout) {
56009             return; // should not happen..
56010         }
56011         var reg = false;
56012         for (var r in this.layout.regions) {
56013             reg = this.layout.getRegion(r);
56014             if (reg.getActivePanel()) {
56015                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
56016                 reg.setActivePanel(reg.getActivePanel());
56017                 continue;
56018             }
56019             if (!reg.panels.length) {
56020                 continue;
56021             }
56022             reg.showPanel(reg.getPanel(0));
56023         }
56024         
56025         
56026         
56027         
56028     },
56029     
56030     /**
56031      * Returns the nested BorderLayout for this panel
56032      * @return {Roo.BorderLayout} 
56033      */
56034     getLayout : function(){
56035         return this.layout;
56036     },
56037     
56038      /**
56039      * Adds a xtype elements to the layout of the nested panel
56040      * <pre><code>
56041
56042 panel.addxtype({
56043        xtype : 'ContentPanel',
56044        region: 'west',
56045        items: [ .... ]
56046    }
56047 );
56048
56049 panel.addxtype({
56050         xtype : 'NestedLayoutPanel',
56051         region: 'west',
56052         layout: {
56053            center: { },
56054            west: { }   
56055         },
56056         items : [ ... list of content panels or nested layout panels.. ]
56057    }
56058 );
56059 </code></pre>
56060      * @param {Object} cfg Xtype definition of item to add.
56061      */
56062     addxtype : function(cfg) {
56063         return this.layout.addxtype(cfg);
56064     
56065     }
56066 });
56067
56068 Roo.ScrollPanel = function(el, config, content){
56069     config = config || {};
56070     config.fitToFrame = true;
56071     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
56072     
56073     this.el.dom.style.overflow = "hidden";
56074     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
56075     this.el.removeClass("x-layout-inactive-content");
56076     this.el.on("mousewheel", this.onWheel, this);
56077
56078     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
56079     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
56080     up.unselectable(); down.unselectable();
56081     up.on("click", this.scrollUp, this);
56082     down.on("click", this.scrollDown, this);
56083     up.addClassOnOver("x-scroller-btn-over");
56084     down.addClassOnOver("x-scroller-btn-over");
56085     up.addClassOnClick("x-scroller-btn-click");
56086     down.addClassOnClick("x-scroller-btn-click");
56087     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
56088
56089     this.resizeEl = this.el;
56090     this.el = wrap; this.up = up; this.down = down;
56091 };
56092
56093 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
56094     increment : 100,
56095     wheelIncrement : 5,
56096     scrollUp : function(){
56097         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
56098     },
56099
56100     scrollDown : function(){
56101         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
56102     },
56103
56104     afterScroll : function(){
56105         var el = this.resizeEl;
56106         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
56107         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56108         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56109     },
56110
56111     setSize : function(){
56112         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
56113         this.afterScroll();
56114     },
56115
56116     onWheel : function(e){
56117         var d = e.getWheelDelta();
56118         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
56119         this.afterScroll();
56120         e.stopEvent();
56121     },
56122
56123     setContent : function(content, loadScripts){
56124         this.resizeEl.update(content, loadScripts);
56125     }
56126
56127 });
56128
56129
56130
56131 /**
56132  * @class Roo.TreePanel
56133  * @extends Roo.ContentPanel
56134  * Treepanel component
56135  * 
56136  * @constructor
56137  * Create a new TreePanel. - defaults to fit/scoll contents.
56138  * @param {String/Object} config A string to set only the panel's title, or a config object
56139  */
56140 Roo.TreePanel = function(config){
56141     var el = config.el;
56142     var tree = config.tree;
56143     delete config.tree; 
56144     delete config.el; // hopefull!
56145     
56146     // wrapper for IE7 strict & safari scroll issue
56147     
56148     var treeEl = el.createChild();
56149     config.resizeEl = treeEl;
56150     
56151     
56152     
56153     Roo.TreePanel.superclass.constructor.call(this, el, config);
56154  
56155  
56156     this.tree = new Roo.tree.TreePanel(treeEl , tree);
56157     //console.log(tree);
56158     this.on('activate', function()
56159     {
56160         if (this.tree.rendered) {
56161             return;
56162         }
56163         //console.log('render tree');
56164         this.tree.render();
56165     });
56166     // this should not be needed.. - it's actually the 'el' that resizes?
56167     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
56168     
56169     //this.on('resize',  function (cp, w, h) {
56170     //        this.tree.innerCt.setWidth(w);
56171     //        this.tree.innerCt.setHeight(h);
56172     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
56173     //});
56174
56175         
56176     
56177 };
56178
56179 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
56180     fitToFrame : true,
56181     autoScroll : true,
56182     /*
56183      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
56184      */
56185     tree : false
56186
56187 });
56188
56189
56190
56191
56192
56193
56194
56195
56196
56197
56198
56199 /*
56200  * Based on:
56201  * Ext JS Library 1.1.1
56202  * Copyright(c) 2006-2007, Ext JS, LLC.
56203  *
56204  * Originally Released Under LGPL - original licence link has changed is not relivant.
56205  *
56206  * Fork - LGPL
56207  * <script type="text/javascript">
56208  */
56209  
56210
56211 /**
56212  * @class Roo.ReaderLayout
56213  * @extends Roo.BorderLayout
56214  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
56215  * center region containing two nested regions (a top one for a list view and one for item preview below),
56216  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
56217  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
56218  * expedites the setup of the overall layout and regions for this common application style.
56219  * Example:
56220  <pre><code>
56221 var reader = new Roo.ReaderLayout();
56222 var CP = Roo.ContentPanel;  // shortcut for adding
56223
56224 reader.beginUpdate();
56225 reader.add("north", new CP("north", "North"));
56226 reader.add("west", new CP("west", {title: "West"}));
56227 reader.add("east", new CP("east", {title: "East"}));
56228
56229 reader.regions.listView.add(new CP("listView", "List"));
56230 reader.regions.preview.add(new CP("preview", "Preview"));
56231 reader.endUpdate();
56232 </code></pre>
56233 * @constructor
56234 * Create a new ReaderLayout
56235 * @param {Object} config Configuration options
56236 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
56237 * document.body if omitted)
56238 */
56239 Roo.ReaderLayout = function(config, renderTo){
56240     var c = config || {size:{}};
56241     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
56242         north: c.north !== false ? Roo.apply({
56243             split:false,
56244             initialSize: 32,
56245             titlebar: false
56246         }, c.north) : false,
56247         west: c.west !== false ? Roo.apply({
56248             split:true,
56249             initialSize: 200,
56250             minSize: 175,
56251             maxSize: 400,
56252             titlebar: true,
56253             collapsible: true,
56254             animate: true,
56255             margins:{left:5,right:0,bottom:5,top:5},
56256             cmargins:{left:5,right:5,bottom:5,top:5}
56257         }, c.west) : false,
56258         east: c.east !== false ? Roo.apply({
56259             split:true,
56260             initialSize: 200,
56261             minSize: 175,
56262             maxSize: 400,
56263             titlebar: true,
56264             collapsible: true,
56265             animate: true,
56266             margins:{left:0,right:5,bottom:5,top:5},
56267             cmargins:{left:5,right:5,bottom:5,top:5}
56268         }, c.east) : false,
56269         center: Roo.apply({
56270             tabPosition: 'top',
56271             autoScroll:false,
56272             closeOnTab: true,
56273             titlebar:false,
56274             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56275         }, c.center)
56276     });
56277
56278     this.el.addClass('x-reader');
56279
56280     this.beginUpdate();
56281
56282     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56283         south: c.preview !== false ? Roo.apply({
56284             split:true,
56285             initialSize: 200,
56286             minSize: 100,
56287             autoScroll:true,
56288             collapsible:true,
56289             titlebar: true,
56290             cmargins:{top:5,left:0, right:0, bottom:0}
56291         }, c.preview) : false,
56292         center: Roo.apply({
56293             autoScroll:false,
56294             titlebar:false,
56295             minHeight:200
56296         }, c.listView)
56297     });
56298     this.add('center', new Roo.NestedLayoutPanel(inner,
56299             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56300
56301     this.endUpdate();
56302
56303     this.regions.preview = inner.getRegion('south');
56304     this.regions.listView = inner.getRegion('center');
56305 };
56306
56307 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56308  * Based on:
56309  * Ext JS Library 1.1.1
56310  * Copyright(c) 2006-2007, Ext JS, LLC.
56311  *
56312  * Originally Released Under LGPL - original licence link has changed is not relivant.
56313  *
56314  * Fork - LGPL
56315  * <script type="text/javascript">
56316  */
56317  
56318 /**
56319  * @class Roo.grid.Grid
56320  * @extends Roo.util.Observable
56321  * This class represents the primary interface of a component based grid control.
56322  * <br><br>Usage:<pre><code>
56323  var grid = new Roo.grid.Grid("my-container-id", {
56324      ds: myDataStore,
56325      cm: myColModel,
56326      selModel: mySelectionModel,
56327      autoSizeColumns: true,
56328      monitorWindowResize: false,
56329      trackMouseOver: true
56330  });
56331  // set any options
56332  grid.render();
56333  * </code></pre>
56334  * <b>Common Problems:</b><br/>
56335  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56336  * element will correct this<br/>
56337  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56338  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56339  * are unpredictable.<br/>
56340  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56341  * grid to calculate dimensions/offsets.<br/>
56342   * @constructor
56343  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56344  * The container MUST have some type of size defined for the grid to fill. The container will be
56345  * automatically set to position relative if it isn't already.
56346  * @param {Object} config A config object that sets properties on this grid.
56347  */
56348 Roo.grid.Grid = function(container, config){
56349         // initialize the container
56350         this.container = Roo.get(container);
56351         this.container.update("");
56352         this.container.setStyle("overflow", "hidden");
56353     this.container.addClass('x-grid-container');
56354
56355     this.id = this.container.id;
56356
56357     Roo.apply(this, config);
56358     // check and correct shorthanded configs
56359     if(this.ds){
56360         this.dataSource = this.ds;
56361         delete this.ds;
56362     }
56363     if(this.cm){
56364         this.colModel = this.cm;
56365         delete this.cm;
56366     }
56367     if(this.sm){
56368         this.selModel = this.sm;
56369         delete this.sm;
56370     }
56371
56372     if (this.selModel) {
56373         this.selModel = Roo.factory(this.selModel, Roo.grid);
56374         this.sm = this.selModel;
56375         this.sm.xmodule = this.xmodule || false;
56376     }
56377     if (typeof(this.colModel.config) == 'undefined') {
56378         this.colModel = new Roo.grid.ColumnModel(this.colModel);
56379         this.cm = this.colModel;
56380         this.cm.xmodule = this.xmodule || false;
56381     }
56382     if (this.dataSource) {
56383         this.dataSource= Roo.factory(this.dataSource, Roo.data);
56384         this.ds = this.dataSource;
56385         this.ds.xmodule = this.xmodule || false;
56386          
56387     }
56388     
56389     
56390     
56391     if(this.width){
56392         this.container.setWidth(this.width);
56393     }
56394
56395     if(this.height){
56396         this.container.setHeight(this.height);
56397     }
56398     /** @private */
56399         this.addEvents({
56400         // raw events
56401         /**
56402          * @event click
56403          * The raw click event for the entire grid.
56404          * @param {Roo.EventObject} e
56405          */
56406         "click" : true,
56407         /**
56408          * @event dblclick
56409          * The raw dblclick event for the entire grid.
56410          * @param {Roo.EventObject} e
56411          */
56412         "dblclick" : true,
56413         /**
56414          * @event contextmenu
56415          * The raw contextmenu event for the entire grid.
56416          * @param {Roo.EventObject} e
56417          */
56418         "contextmenu" : true,
56419         /**
56420          * @event mousedown
56421          * The raw mousedown event for the entire grid.
56422          * @param {Roo.EventObject} e
56423          */
56424         "mousedown" : true,
56425         /**
56426          * @event mouseup
56427          * The raw mouseup event for the entire grid.
56428          * @param {Roo.EventObject} e
56429          */
56430         "mouseup" : true,
56431         /**
56432          * @event mouseover
56433          * The raw mouseover event for the entire grid.
56434          * @param {Roo.EventObject} e
56435          */
56436         "mouseover" : true,
56437         /**
56438          * @event mouseout
56439          * The raw mouseout event for the entire grid.
56440          * @param {Roo.EventObject} e
56441          */
56442         "mouseout" : true,
56443         /**
56444          * @event keypress
56445          * The raw keypress event for the entire grid.
56446          * @param {Roo.EventObject} e
56447          */
56448         "keypress" : true,
56449         /**
56450          * @event keydown
56451          * The raw keydown event for the entire grid.
56452          * @param {Roo.EventObject} e
56453          */
56454         "keydown" : true,
56455
56456         // custom events
56457
56458         /**
56459          * @event cellclick
56460          * Fires when a cell is clicked
56461          * @param {Grid} this
56462          * @param {Number} rowIndex
56463          * @param {Number} columnIndex
56464          * @param {Roo.EventObject} e
56465          */
56466         "cellclick" : true,
56467         /**
56468          * @event celldblclick
56469          * Fires when a cell is double clicked
56470          * @param {Grid} this
56471          * @param {Number} rowIndex
56472          * @param {Number} columnIndex
56473          * @param {Roo.EventObject} e
56474          */
56475         "celldblclick" : true,
56476         /**
56477          * @event rowclick
56478          * Fires when a row is clicked
56479          * @param {Grid} this
56480          * @param {Number} rowIndex
56481          * @param {Roo.EventObject} e
56482          */
56483         "rowclick" : true,
56484         /**
56485          * @event rowdblclick
56486          * Fires when a row is double clicked
56487          * @param {Grid} this
56488          * @param {Number} rowIndex
56489          * @param {Roo.EventObject} e
56490          */
56491         "rowdblclick" : true,
56492         /**
56493          * @event headerclick
56494          * Fires when a header is clicked
56495          * @param {Grid} this
56496          * @param {Number} columnIndex
56497          * @param {Roo.EventObject} e
56498          */
56499         "headerclick" : true,
56500         /**
56501          * @event headerdblclick
56502          * Fires when a header cell is double clicked
56503          * @param {Grid} this
56504          * @param {Number} columnIndex
56505          * @param {Roo.EventObject} e
56506          */
56507         "headerdblclick" : true,
56508         /**
56509          * @event rowcontextmenu
56510          * Fires when a row is right clicked
56511          * @param {Grid} this
56512          * @param {Number} rowIndex
56513          * @param {Roo.EventObject} e
56514          */
56515         "rowcontextmenu" : true,
56516         /**
56517          * @event cellcontextmenu
56518          * Fires when a cell is right clicked
56519          * @param {Grid} this
56520          * @param {Number} rowIndex
56521          * @param {Number} cellIndex
56522          * @param {Roo.EventObject} e
56523          */
56524          "cellcontextmenu" : true,
56525         /**
56526          * @event headercontextmenu
56527          * Fires when a header is right clicked
56528          * @param {Grid} this
56529          * @param {Number} columnIndex
56530          * @param {Roo.EventObject} e
56531          */
56532         "headercontextmenu" : true,
56533         /**
56534          * @event bodyscroll
56535          * Fires when the body element is scrolled
56536          * @param {Number} scrollLeft
56537          * @param {Number} scrollTop
56538          */
56539         "bodyscroll" : true,
56540         /**
56541          * @event columnresize
56542          * Fires when the user resizes a column
56543          * @param {Number} columnIndex
56544          * @param {Number} newSize
56545          */
56546         "columnresize" : true,
56547         /**
56548          * @event columnmove
56549          * Fires when the user moves a column
56550          * @param {Number} oldIndex
56551          * @param {Number} newIndex
56552          */
56553         "columnmove" : true,
56554         /**
56555          * @event startdrag
56556          * Fires when row(s) start being dragged
56557          * @param {Grid} this
56558          * @param {Roo.GridDD} dd The drag drop object
56559          * @param {event} e The raw browser event
56560          */
56561         "startdrag" : true,
56562         /**
56563          * @event enddrag
56564          * Fires when a drag operation is complete
56565          * @param {Grid} this
56566          * @param {Roo.GridDD} dd The drag drop object
56567          * @param {event} e The raw browser event
56568          */
56569         "enddrag" : true,
56570         /**
56571          * @event dragdrop
56572          * Fires when dragged row(s) are dropped on a valid DD target
56573          * @param {Grid} this
56574          * @param {Roo.GridDD} dd The drag drop object
56575          * @param {String} targetId The target drag drop object
56576          * @param {event} e The raw browser event
56577          */
56578         "dragdrop" : true,
56579         /**
56580          * @event dragover
56581          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56582          * @param {Grid} this
56583          * @param {Roo.GridDD} dd The drag drop object
56584          * @param {String} targetId The target drag drop object
56585          * @param {event} e The raw browser event
56586          */
56587         "dragover" : true,
56588         /**
56589          * @event dragenter
56590          *  Fires when the dragged row(s) first cross another DD target while being dragged
56591          * @param {Grid} this
56592          * @param {Roo.GridDD} dd The drag drop object
56593          * @param {String} targetId The target drag drop object
56594          * @param {event} e The raw browser event
56595          */
56596         "dragenter" : true,
56597         /**
56598          * @event dragout
56599          * Fires when the dragged row(s) leave another DD target while being dragged
56600          * @param {Grid} this
56601          * @param {Roo.GridDD} dd The drag drop object
56602          * @param {String} targetId The target drag drop object
56603          * @param {event} e The raw browser event
56604          */
56605         "dragout" : true,
56606         /**
56607          * @event rowclass
56608          * Fires when a row is rendered, so you can change add a style to it.
56609          * @param {GridView} gridview   The grid view
56610          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56611          */
56612         'rowclass' : true,
56613
56614         /**
56615          * @event render
56616          * Fires when the grid is rendered
56617          * @param {Grid} grid
56618          */
56619         'render' : true
56620     });
56621
56622     Roo.grid.Grid.superclass.constructor.call(this);
56623 };
56624 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
56625     
56626     /**
56627          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
56628          */
56629         /**
56630          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
56631          */
56632         /**
56633          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
56634          */
56635         /**
56636          * @cfg {Roo.grid.Store} ds The data store for the grid
56637          */
56638         /**
56639          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
56640          */
56641         /**
56642      * @cfg {String} ddGroup - drag drop group.
56643      */
56644       /**
56645      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
56646      */
56647
56648     /**
56649      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
56650      */
56651     minColumnWidth : 25,
56652
56653     /**
56654      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
56655      * <b>on initial render.</b> It is more efficient to explicitly size the columns
56656      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
56657      */
56658     autoSizeColumns : false,
56659
56660     /**
56661      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
56662      */
56663     autoSizeHeaders : true,
56664
56665     /**
56666      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
56667      */
56668     monitorWindowResize : true,
56669
56670     /**
56671      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
56672      * rows measured to get a columns size. Default is 0 (all rows).
56673      */
56674     maxRowsToMeasure : 0,
56675
56676     /**
56677      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
56678      */
56679     trackMouseOver : true,
56680
56681     /**
56682     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
56683     */
56684       /**
56685     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
56686     */
56687     
56688     /**
56689     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
56690     */
56691     enableDragDrop : false,
56692     
56693     /**
56694     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
56695     */
56696     enableColumnMove : true,
56697     
56698     /**
56699     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
56700     */
56701     enableColumnHide : true,
56702     
56703     /**
56704     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
56705     */
56706     enableRowHeightSync : false,
56707     
56708     /**
56709     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
56710     */
56711     stripeRows : true,
56712     
56713     /**
56714     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
56715     */
56716     autoHeight : false,
56717
56718     /**
56719      * @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.
56720      */
56721     autoExpandColumn : false,
56722
56723     /**
56724     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
56725     * Default is 50.
56726     */
56727     autoExpandMin : 50,
56728
56729     /**
56730     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
56731     */
56732     autoExpandMax : 1000,
56733
56734     /**
56735     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
56736     */
56737     view : null,
56738
56739     /**
56740     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
56741     */
56742     loadMask : false,
56743     /**
56744     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
56745     */
56746     dropTarget: false,
56747     
56748    
56749     
56750     // private
56751     rendered : false,
56752
56753     /**
56754     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
56755     * of a fixed width. Default is false.
56756     */
56757     /**
56758     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
56759     */
56760     
56761     
56762     /**
56763     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
56764     * %0 is replaced with the number of selected rows.
56765     */
56766     ddText : "{0} selected row{1}",
56767     
56768     
56769     /**
56770      * Called once after all setup has been completed and the grid is ready to be rendered.
56771      * @return {Roo.grid.Grid} this
56772      */
56773     render : function()
56774     {
56775         var c = this.container;
56776         // try to detect autoHeight/width mode
56777         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
56778             this.autoHeight = true;
56779         }
56780         var view = this.getView();
56781         view.init(this);
56782
56783         c.on("click", this.onClick, this);
56784         c.on("dblclick", this.onDblClick, this);
56785         c.on("contextmenu", this.onContextMenu, this);
56786         c.on("keydown", this.onKeyDown, this);
56787         if (Roo.isTouch) {
56788             c.on("touchstart", this.onTouchStart, this);
56789         }
56790
56791         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
56792
56793         this.getSelectionModel().init(this);
56794
56795         view.render();
56796
56797         if(this.loadMask){
56798             this.loadMask = new Roo.LoadMask(this.container,
56799                     Roo.apply({store:this.dataSource}, this.loadMask));
56800         }
56801         
56802         
56803         if (this.toolbar && this.toolbar.xtype) {
56804             this.toolbar.container = this.getView().getHeaderPanel(true);
56805             this.toolbar = new Roo.Toolbar(this.toolbar);
56806         }
56807         if (this.footer && this.footer.xtype) {
56808             this.footer.dataSource = this.getDataSource();
56809             this.footer.container = this.getView().getFooterPanel(true);
56810             this.footer = Roo.factory(this.footer, Roo);
56811         }
56812         if (this.dropTarget && this.dropTarget.xtype) {
56813             delete this.dropTarget.xtype;
56814             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
56815         }
56816         
56817         
56818         this.rendered = true;
56819         this.fireEvent('render', this);
56820         return this;
56821     },
56822
56823     /**
56824      * Reconfigures the grid to use a different Store and Column Model.
56825      * The View will be bound to the new objects and refreshed.
56826      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
56827      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
56828      */
56829     reconfigure : function(dataSource, colModel){
56830         if(this.loadMask){
56831             this.loadMask.destroy();
56832             this.loadMask = new Roo.LoadMask(this.container,
56833                     Roo.apply({store:dataSource}, this.loadMask));
56834         }
56835         this.view.bind(dataSource, colModel);
56836         this.dataSource = dataSource;
56837         this.colModel = colModel;
56838         this.view.refresh(true);
56839     },
56840     /**
56841      * addColumns
56842      * Add's a column, default at the end..
56843      
56844      * @param {int} position to add (default end)
56845      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
56846      */
56847     addColumns : function(pos, ar)
56848     {
56849         
56850         for (var i =0;i< ar.length;i++) {
56851             var cfg = ar[i];
56852             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
56853             this.cm.lookup[cfg.id] = cfg;
56854         }
56855         
56856         
56857         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
56858             pos = this.cm.config.length; //this.cm.config.push(cfg);
56859         } 
56860         pos = Math.max(0,pos);
56861         ar.unshift(0);
56862         ar.unshift(pos);
56863         this.cm.config.splice.apply(this.cm.config, ar);
56864         
56865         
56866         
56867         this.view.generateRules(this.cm);
56868         this.view.refresh(true);
56869         
56870     },
56871     
56872     
56873     
56874     
56875     // private
56876     onKeyDown : function(e){
56877         this.fireEvent("keydown", e);
56878     },
56879
56880     /**
56881      * Destroy this grid.
56882      * @param {Boolean} removeEl True to remove the element
56883      */
56884     destroy : function(removeEl, keepListeners){
56885         if(this.loadMask){
56886             this.loadMask.destroy();
56887         }
56888         var c = this.container;
56889         c.removeAllListeners();
56890         this.view.destroy();
56891         this.colModel.purgeListeners();
56892         if(!keepListeners){
56893             this.purgeListeners();
56894         }
56895         c.update("");
56896         if(removeEl === true){
56897             c.remove();
56898         }
56899     },
56900
56901     // private
56902     processEvent : function(name, e){
56903         // does this fire select???
56904         //Roo.log('grid:processEvent '  + name);
56905         
56906         if (name != 'touchstart' ) {
56907             this.fireEvent(name, e);    
56908         }
56909         
56910         var t = e.getTarget();
56911         var v = this.view;
56912         var header = v.findHeaderIndex(t);
56913         if(header !== false){
56914             var ename = name == 'touchstart' ? 'click' : name;
56915              
56916             this.fireEvent("header" + ename, this, header, e);
56917         }else{
56918             var row = v.findRowIndex(t);
56919             var cell = v.findCellIndex(t);
56920             if (name == 'touchstart') {
56921                 // first touch is always a click.
56922                 // hopefull this happens after selection is updated.?
56923                 name = false;
56924                 
56925                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
56926                     var cs = this.selModel.getSelectedCell();
56927                     if (row == cs[0] && cell == cs[1]){
56928                         name = 'dblclick';
56929                     }
56930                 }
56931                 if (typeof(this.selModel.getSelections) != 'undefined') {
56932                     var cs = this.selModel.getSelections();
56933                     var ds = this.dataSource;
56934                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
56935                         name = 'dblclick';
56936                     }
56937                 }
56938                 if (!name) {
56939                     return;
56940                 }
56941             }
56942             
56943             
56944             if(row !== false){
56945                 this.fireEvent("row" + name, this, row, e);
56946                 if(cell !== false){
56947                     this.fireEvent("cell" + name, this, row, cell, e);
56948                 }
56949             }
56950         }
56951     },
56952
56953     // private
56954     onClick : function(e){
56955         this.processEvent("click", e);
56956     },
56957    // private
56958     onTouchStart : function(e){
56959         this.processEvent("touchstart", e);
56960     },
56961
56962     // private
56963     onContextMenu : function(e, t){
56964         this.processEvent("contextmenu", e);
56965     },
56966
56967     // private
56968     onDblClick : function(e){
56969         this.processEvent("dblclick", e);
56970     },
56971
56972     // private
56973     walkCells : function(row, col, step, fn, scope){
56974         var cm = this.colModel, clen = cm.getColumnCount();
56975         var ds = this.dataSource, rlen = ds.getCount(), first = true;
56976         if(step < 0){
56977             if(col < 0){
56978                 row--;
56979                 first = false;
56980             }
56981             while(row >= 0){
56982                 if(!first){
56983                     col = clen-1;
56984                 }
56985                 first = false;
56986                 while(col >= 0){
56987                     if(fn.call(scope || this, row, col, cm) === true){
56988                         return [row, col];
56989                     }
56990                     col--;
56991                 }
56992                 row--;
56993             }
56994         } else {
56995             if(col >= clen){
56996                 row++;
56997                 first = false;
56998             }
56999             while(row < rlen){
57000                 if(!first){
57001                     col = 0;
57002                 }
57003                 first = false;
57004                 while(col < clen){
57005                     if(fn.call(scope || this, row, col, cm) === true){
57006                         return [row, col];
57007                     }
57008                     col++;
57009                 }
57010                 row++;
57011             }
57012         }
57013         return null;
57014     },
57015
57016     // private
57017     getSelections : function(){
57018         return this.selModel.getSelections();
57019     },
57020
57021     /**
57022      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
57023      * but if manual update is required this method will initiate it.
57024      */
57025     autoSize : function(){
57026         if(this.rendered){
57027             this.view.layout();
57028             if(this.view.adjustForScroll){
57029                 this.view.adjustForScroll();
57030             }
57031         }
57032     },
57033
57034     /**
57035      * Returns the grid's underlying element.
57036      * @return {Element} The element
57037      */
57038     getGridEl : function(){
57039         return this.container;
57040     },
57041
57042     // private for compatibility, overridden by editor grid
57043     stopEditing : function(){},
57044
57045     /**
57046      * Returns the grid's SelectionModel.
57047      * @return {SelectionModel}
57048      */
57049     getSelectionModel : function(){
57050         if(!this.selModel){
57051             this.selModel = new Roo.grid.RowSelectionModel();
57052         }
57053         return this.selModel;
57054     },
57055
57056     /**
57057      * Returns the grid's DataSource.
57058      * @return {DataSource}
57059      */
57060     getDataSource : function(){
57061         return this.dataSource;
57062     },
57063
57064     /**
57065      * Returns the grid's ColumnModel.
57066      * @return {ColumnModel}
57067      */
57068     getColumnModel : function(){
57069         return this.colModel;
57070     },
57071
57072     /**
57073      * Returns the grid's GridView object.
57074      * @return {GridView}
57075      */
57076     getView : function(){
57077         if(!this.view){
57078             this.view = new Roo.grid.GridView(this.viewConfig);
57079             this.relayEvents(this.view, [
57080                 "beforerowremoved", "beforerowsinserted",
57081                 "beforerefresh", "rowremoved",
57082                 "rowsinserted", "rowupdated" ,"refresh"
57083             ]);
57084         }
57085         return this.view;
57086     },
57087     /**
57088      * Called to get grid's drag proxy text, by default returns this.ddText.
57089      * Override this to put something different in the dragged text.
57090      * @return {String}
57091      */
57092     getDragDropText : function(){
57093         var count = this.selModel.getCount();
57094         return String.format(this.ddText, count, count == 1 ? '' : 's');
57095     }
57096 });
57097 /*
57098  * Based on:
57099  * Ext JS Library 1.1.1
57100  * Copyright(c) 2006-2007, Ext JS, LLC.
57101  *
57102  * Originally Released Under LGPL - original licence link has changed is not relivant.
57103  *
57104  * Fork - LGPL
57105  * <script type="text/javascript">
57106  */
57107  /**
57108  * @class Roo.grid.AbstractGridView
57109  * @extends Roo.util.Observable
57110  * @abstract
57111  * Abstract base class for grid Views
57112  * @constructor
57113  */
57114 Roo.grid.AbstractGridView = function(){
57115         this.grid = null;
57116         
57117         this.events = {
57118             "beforerowremoved" : true,
57119             "beforerowsinserted" : true,
57120             "beforerefresh" : true,
57121             "rowremoved" : true,
57122             "rowsinserted" : true,
57123             "rowupdated" : true,
57124             "refresh" : true
57125         };
57126     Roo.grid.AbstractGridView.superclass.constructor.call(this);
57127 };
57128
57129 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
57130     rowClass : "x-grid-row",
57131     cellClass : "x-grid-cell",
57132     tdClass : "x-grid-td",
57133     hdClass : "x-grid-hd",
57134     splitClass : "x-grid-hd-split",
57135     
57136     init: function(grid){
57137         this.grid = grid;
57138                 var cid = this.grid.getGridEl().id;
57139         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
57140         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
57141         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
57142         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
57143         },
57144         
57145     getColumnRenderers : function(){
57146         var renderers = [];
57147         var cm = this.grid.colModel;
57148         var colCount = cm.getColumnCount();
57149         for(var i = 0; i < colCount; i++){
57150             renderers[i] = cm.getRenderer(i);
57151         }
57152         return renderers;
57153     },
57154     
57155     getColumnIds : function(){
57156         var ids = [];
57157         var cm = this.grid.colModel;
57158         var colCount = cm.getColumnCount();
57159         for(var i = 0; i < colCount; i++){
57160             ids[i] = cm.getColumnId(i);
57161         }
57162         return ids;
57163     },
57164     
57165     getDataIndexes : function(){
57166         if(!this.indexMap){
57167             this.indexMap = this.buildIndexMap();
57168         }
57169         return this.indexMap.colToData;
57170     },
57171     
57172     getColumnIndexByDataIndex : function(dataIndex){
57173         if(!this.indexMap){
57174             this.indexMap = this.buildIndexMap();
57175         }
57176         return this.indexMap.dataToCol[dataIndex];
57177     },
57178     
57179     /**
57180      * Set a css style for a column dynamically. 
57181      * @param {Number} colIndex The index of the column
57182      * @param {String} name The css property name
57183      * @param {String} value The css value
57184      */
57185     setCSSStyle : function(colIndex, name, value){
57186         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
57187         Roo.util.CSS.updateRule(selector, name, value);
57188     },
57189     
57190     generateRules : function(cm){
57191         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
57192         Roo.util.CSS.removeStyleSheet(rulesId);
57193         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57194             var cid = cm.getColumnId(i);
57195             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
57196                          this.tdSelector, cid, " {\n}\n",
57197                          this.hdSelector, cid, " {\n}\n",
57198                          this.splitSelector, cid, " {\n}\n");
57199         }
57200         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57201     }
57202 });/*
57203  * Based on:
57204  * Ext JS Library 1.1.1
57205  * Copyright(c) 2006-2007, Ext JS, LLC.
57206  *
57207  * Originally Released Under LGPL - original licence link has changed is not relivant.
57208  *
57209  * Fork - LGPL
57210  * <script type="text/javascript">
57211  */
57212
57213 // private
57214 // This is a support class used internally by the Grid components
57215 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
57216     this.grid = grid;
57217     this.view = grid.getView();
57218     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57219     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
57220     if(hd2){
57221         this.setHandleElId(Roo.id(hd));
57222         this.setOuterHandleElId(Roo.id(hd2));
57223     }
57224     this.scroll = false;
57225 };
57226 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
57227     maxDragWidth: 120,
57228     getDragData : function(e){
57229         var t = Roo.lib.Event.getTarget(e);
57230         var h = this.view.findHeaderCell(t);
57231         if(h){
57232             return {ddel: h.firstChild, header:h};
57233         }
57234         return false;
57235     },
57236
57237     onInitDrag : function(e){
57238         this.view.headersDisabled = true;
57239         var clone = this.dragData.ddel.cloneNode(true);
57240         clone.id = Roo.id();
57241         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
57242         this.proxy.update(clone);
57243         return true;
57244     },
57245
57246     afterValidDrop : function(){
57247         var v = this.view;
57248         setTimeout(function(){
57249             v.headersDisabled = false;
57250         }, 50);
57251     },
57252
57253     afterInvalidDrop : function(){
57254         var v = this.view;
57255         setTimeout(function(){
57256             v.headersDisabled = false;
57257         }, 50);
57258     }
57259 });
57260 /*
57261  * Based on:
57262  * Ext JS Library 1.1.1
57263  * Copyright(c) 2006-2007, Ext JS, LLC.
57264  *
57265  * Originally Released Under LGPL - original licence link has changed is not relivant.
57266  *
57267  * Fork - LGPL
57268  * <script type="text/javascript">
57269  */
57270 // private
57271 // This is a support class used internally by the Grid components
57272 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57273     this.grid = grid;
57274     this.view = grid.getView();
57275     // split the proxies so they don't interfere with mouse events
57276     this.proxyTop = Roo.DomHelper.append(document.body, {
57277         cls:"col-move-top", html:"&#160;"
57278     }, true);
57279     this.proxyBottom = Roo.DomHelper.append(document.body, {
57280         cls:"col-move-bottom", html:"&#160;"
57281     }, true);
57282     this.proxyTop.hide = this.proxyBottom.hide = function(){
57283         this.setLeftTop(-100,-100);
57284         this.setStyle("visibility", "hidden");
57285     };
57286     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57287     // temporarily disabled
57288     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57289     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57290 };
57291 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57292     proxyOffsets : [-4, -9],
57293     fly: Roo.Element.fly,
57294
57295     getTargetFromEvent : function(e){
57296         var t = Roo.lib.Event.getTarget(e);
57297         var cindex = this.view.findCellIndex(t);
57298         if(cindex !== false){
57299             return this.view.getHeaderCell(cindex);
57300         }
57301         return null;
57302     },
57303
57304     nextVisible : function(h){
57305         var v = this.view, cm = this.grid.colModel;
57306         h = h.nextSibling;
57307         while(h){
57308             if(!cm.isHidden(v.getCellIndex(h))){
57309                 return h;
57310             }
57311             h = h.nextSibling;
57312         }
57313         return null;
57314     },
57315
57316     prevVisible : function(h){
57317         var v = this.view, cm = this.grid.colModel;
57318         h = h.prevSibling;
57319         while(h){
57320             if(!cm.isHidden(v.getCellIndex(h))){
57321                 return h;
57322             }
57323             h = h.prevSibling;
57324         }
57325         return null;
57326     },
57327
57328     positionIndicator : function(h, n, e){
57329         var x = Roo.lib.Event.getPageX(e);
57330         var r = Roo.lib.Dom.getRegion(n.firstChild);
57331         var px, pt, py = r.top + this.proxyOffsets[1];
57332         if((r.right - x) <= (r.right-r.left)/2){
57333             px = r.right+this.view.borderWidth;
57334             pt = "after";
57335         }else{
57336             px = r.left;
57337             pt = "before";
57338         }
57339         var oldIndex = this.view.getCellIndex(h);
57340         var newIndex = this.view.getCellIndex(n);
57341
57342         if(this.grid.colModel.isFixed(newIndex)){
57343             return false;
57344         }
57345
57346         var locked = this.grid.colModel.isLocked(newIndex);
57347
57348         if(pt == "after"){
57349             newIndex++;
57350         }
57351         if(oldIndex < newIndex){
57352             newIndex--;
57353         }
57354         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
57355             return false;
57356         }
57357         px +=  this.proxyOffsets[0];
57358         this.proxyTop.setLeftTop(px, py);
57359         this.proxyTop.show();
57360         if(!this.bottomOffset){
57361             this.bottomOffset = this.view.mainHd.getHeight();
57362         }
57363         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
57364         this.proxyBottom.show();
57365         return pt;
57366     },
57367
57368     onNodeEnter : function(n, dd, e, data){
57369         if(data.header != n){
57370             this.positionIndicator(data.header, n, e);
57371         }
57372     },
57373
57374     onNodeOver : function(n, dd, e, data){
57375         var result = false;
57376         if(data.header != n){
57377             result = this.positionIndicator(data.header, n, e);
57378         }
57379         if(!result){
57380             this.proxyTop.hide();
57381             this.proxyBottom.hide();
57382         }
57383         return result ? this.dropAllowed : this.dropNotAllowed;
57384     },
57385
57386     onNodeOut : function(n, dd, e, data){
57387         this.proxyTop.hide();
57388         this.proxyBottom.hide();
57389     },
57390
57391     onNodeDrop : function(n, dd, e, data){
57392         var h = data.header;
57393         if(h != n){
57394             var cm = this.grid.colModel;
57395             var x = Roo.lib.Event.getPageX(e);
57396             var r = Roo.lib.Dom.getRegion(n.firstChild);
57397             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
57398             var oldIndex = this.view.getCellIndex(h);
57399             var newIndex = this.view.getCellIndex(n);
57400             var locked = cm.isLocked(newIndex);
57401             if(pt == "after"){
57402                 newIndex++;
57403             }
57404             if(oldIndex < newIndex){
57405                 newIndex--;
57406             }
57407             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
57408                 return false;
57409             }
57410             cm.setLocked(oldIndex, locked, true);
57411             cm.moveColumn(oldIndex, newIndex);
57412             this.grid.fireEvent("columnmove", oldIndex, newIndex);
57413             return true;
57414         }
57415         return false;
57416     }
57417 });
57418 /*
57419  * Based on:
57420  * Ext JS Library 1.1.1
57421  * Copyright(c) 2006-2007, Ext JS, LLC.
57422  *
57423  * Originally Released Under LGPL - original licence link has changed is not relivant.
57424  *
57425  * Fork - LGPL
57426  * <script type="text/javascript">
57427  */
57428   
57429 /**
57430  * @class Roo.grid.GridView
57431  * @extends Roo.util.Observable
57432  *
57433  * @constructor
57434  * @param {Object} config
57435  */
57436 Roo.grid.GridView = function(config){
57437     Roo.grid.GridView.superclass.constructor.call(this);
57438     this.el = null;
57439
57440     Roo.apply(this, config);
57441 };
57442
57443 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
57444
57445     unselectable :  'unselectable="on"',
57446     unselectableCls :  'x-unselectable',
57447     
57448     
57449     rowClass : "x-grid-row",
57450
57451     cellClass : "x-grid-col",
57452
57453     tdClass : "x-grid-td",
57454
57455     hdClass : "x-grid-hd",
57456
57457     splitClass : "x-grid-split",
57458
57459     sortClasses : ["sort-asc", "sort-desc"],
57460
57461     enableMoveAnim : false,
57462
57463     hlColor: "C3DAF9",
57464
57465     dh : Roo.DomHelper,
57466
57467     fly : Roo.Element.fly,
57468
57469     css : Roo.util.CSS,
57470
57471     borderWidth: 1,
57472
57473     splitOffset: 3,
57474
57475     scrollIncrement : 22,
57476
57477     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
57478
57479     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
57480
57481     bind : function(ds, cm){
57482         if(this.ds){
57483             this.ds.un("load", this.onLoad, this);
57484             this.ds.un("datachanged", this.onDataChange, this);
57485             this.ds.un("add", this.onAdd, this);
57486             this.ds.un("remove", this.onRemove, this);
57487             this.ds.un("update", this.onUpdate, this);
57488             this.ds.un("clear", this.onClear, this);
57489         }
57490         if(ds){
57491             ds.on("load", this.onLoad, this);
57492             ds.on("datachanged", this.onDataChange, this);
57493             ds.on("add", this.onAdd, this);
57494             ds.on("remove", this.onRemove, this);
57495             ds.on("update", this.onUpdate, this);
57496             ds.on("clear", this.onClear, this);
57497         }
57498         this.ds = ds;
57499
57500         if(this.cm){
57501             this.cm.un("widthchange", this.onColWidthChange, this);
57502             this.cm.un("headerchange", this.onHeaderChange, this);
57503             this.cm.un("hiddenchange", this.onHiddenChange, this);
57504             this.cm.un("columnmoved", this.onColumnMove, this);
57505             this.cm.un("columnlockchange", this.onColumnLock, this);
57506         }
57507         if(cm){
57508             this.generateRules(cm);
57509             cm.on("widthchange", this.onColWidthChange, this);
57510             cm.on("headerchange", this.onHeaderChange, this);
57511             cm.on("hiddenchange", this.onHiddenChange, this);
57512             cm.on("columnmoved", this.onColumnMove, this);
57513             cm.on("columnlockchange", this.onColumnLock, this);
57514         }
57515         this.cm = cm;
57516     },
57517
57518     init: function(grid){
57519         Roo.grid.GridView.superclass.init.call(this, grid);
57520
57521         this.bind(grid.dataSource, grid.colModel);
57522
57523         grid.on("headerclick", this.handleHeaderClick, this);
57524
57525         if(grid.trackMouseOver){
57526             grid.on("mouseover", this.onRowOver, this);
57527             grid.on("mouseout", this.onRowOut, this);
57528         }
57529         grid.cancelTextSelection = function(){};
57530         this.gridId = grid.id;
57531
57532         var tpls = this.templates || {};
57533
57534         if(!tpls.master){
57535             tpls.master = new Roo.Template(
57536                '<div class="x-grid" hidefocus="true">',
57537                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
57538                   '<div class="x-grid-topbar"></div>',
57539                   '<div class="x-grid-scroller"><div></div></div>',
57540                   '<div class="x-grid-locked">',
57541                       '<div class="x-grid-header">{lockedHeader}</div>',
57542                       '<div class="x-grid-body">{lockedBody}</div>',
57543                   "</div>",
57544                   '<div class="x-grid-viewport">',
57545                       '<div class="x-grid-header">{header}</div>',
57546                       '<div class="x-grid-body">{body}</div>',
57547                   "</div>",
57548                   '<div class="x-grid-bottombar"></div>',
57549                  
57550                   '<div class="x-grid-resize-proxy">&#160;</div>',
57551                "</div>"
57552             );
57553             tpls.master.disableformats = true;
57554         }
57555
57556         if(!tpls.header){
57557             tpls.header = new Roo.Template(
57558                '<table border="0" cellspacing="0" cellpadding="0">',
57559                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
57560                "</table>{splits}"
57561             );
57562             tpls.header.disableformats = true;
57563         }
57564         tpls.header.compile();
57565
57566         if(!tpls.hcell){
57567             tpls.hcell = new Roo.Template(
57568                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
57569                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
57570                 "</div></td>"
57571              );
57572              tpls.hcell.disableFormats = true;
57573         }
57574         tpls.hcell.compile();
57575
57576         if(!tpls.hsplit){
57577             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
57578                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
57579             tpls.hsplit.disableFormats = true;
57580         }
57581         tpls.hsplit.compile();
57582
57583         if(!tpls.body){
57584             tpls.body = new Roo.Template(
57585                '<table border="0" cellspacing="0" cellpadding="0">',
57586                "<tbody>{rows}</tbody>",
57587                "</table>"
57588             );
57589             tpls.body.disableFormats = true;
57590         }
57591         tpls.body.compile();
57592
57593         if(!tpls.row){
57594             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
57595             tpls.row.disableFormats = true;
57596         }
57597         tpls.row.compile();
57598
57599         if(!tpls.cell){
57600             tpls.cell = new Roo.Template(
57601                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
57602                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
57603                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
57604                 "</td>"
57605             );
57606             tpls.cell.disableFormats = true;
57607         }
57608         tpls.cell.compile();
57609
57610         this.templates = tpls;
57611     },
57612
57613     // remap these for backwards compat
57614     onColWidthChange : function(){
57615         this.updateColumns.apply(this, arguments);
57616     },
57617     onHeaderChange : function(){
57618         this.updateHeaders.apply(this, arguments);
57619     }, 
57620     onHiddenChange : function(){
57621         this.handleHiddenChange.apply(this, arguments);
57622     },
57623     onColumnMove : function(){
57624         this.handleColumnMove.apply(this, arguments);
57625     },
57626     onColumnLock : function(){
57627         this.handleLockChange.apply(this, arguments);
57628     },
57629
57630     onDataChange : function(){
57631         this.refresh();
57632         this.updateHeaderSortState();
57633     },
57634
57635     onClear : function(){
57636         this.refresh();
57637     },
57638
57639     onUpdate : function(ds, record){
57640         this.refreshRow(record);
57641     },
57642
57643     refreshRow : function(record){
57644         var ds = this.ds, index;
57645         if(typeof record == 'number'){
57646             index = record;
57647             record = ds.getAt(index);
57648         }else{
57649             index = ds.indexOf(record);
57650         }
57651         this.insertRows(ds, index, index, true);
57652         this.onRemove(ds, record, index+1, true);
57653         this.syncRowHeights(index, index);
57654         this.layout();
57655         this.fireEvent("rowupdated", this, index, record);
57656     },
57657
57658     onAdd : function(ds, records, index){
57659         this.insertRows(ds, index, index + (records.length-1));
57660     },
57661
57662     onRemove : function(ds, record, index, isUpdate){
57663         if(isUpdate !== true){
57664             this.fireEvent("beforerowremoved", this, index, record);
57665         }
57666         var bt = this.getBodyTable(), lt = this.getLockedTable();
57667         if(bt.rows[index]){
57668             bt.firstChild.removeChild(bt.rows[index]);
57669         }
57670         if(lt.rows[index]){
57671             lt.firstChild.removeChild(lt.rows[index]);
57672         }
57673         if(isUpdate !== true){
57674             this.stripeRows(index);
57675             this.syncRowHeights(index, index);
57676             this.layout();
57677             this.fireEvent("rowremoved", this, index, record);
57678         }
57679     },
57680
57681     onLoad : function(){
57682         this.scrollToTop();
57683     },
57684
57685     /**
57686      * Scrolls the grid to the top
57687      */
57688     scrollToTop : function(){
57689         if(this.scroller){
57690             this.scroller.dom.scrollTop = 0;
57691             this.syncScroll();
57692         }
57693     },
57694
57695     /**
57696      * Gets a panel in the header of the grid that can be used for toolbars etc.
57697      * After modifying the contents of this panel a call to grid.autoSize() may be
57698      * required to register any changes in size.
57699      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
57700      * @return Roo.Element
57701      */
57702     getHeaderPanel : function(doShow){
57703         if(doShow){
57704             this.headerPanel.show();
57705         }
57706         return this.headerPanel;
57707     },
57708
57709     /**
57710      * Gets a panel in the footer of the grid that can be used for toolbars etc.
57711      * After modifying the contents of this panel a call to grid.autoSize() may be
57712      * required to register any changes in size.
57713      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
57714      * @return Roo.Element
57715      */
57716     getFooterPanel : function(doShow){
57717         if(doShow){
57718             this.footerPanel.show();
57719         }
57720         return this.footerPanel;
57721     },
57722
57723     initElements : function(){
57724         var E = Roo.Element;
57725         var el = this.grid.getGridEl().dom.firstChild;
57726         var cs = el.childNodes;
57727
57728         this.el = new E(el);
57729         
57730          this.focusEl = new E(el.firstChild);
57731         this.focusEl.swallowEvent("click", true);
57732         
57733         this.headerPanel = new E(cs[1]);
57734         this.headerPanel.enableDisplayMode("block");
57735
57736         this.scroller = new E(cs[2]);
57737         this.scrollSizer = new E(this.scroller.dom.firstChild);
57738
57739         this.lockedWrap = new E(cs[3]);
57740         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
57741         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
57742
57743         this.mainWrap = new E(cs[4]);
57744         this.mainHd = new E(this.mainWrap.dom.firstChild);
57745         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
57746
57747         this.footerPanel = new E(cs[5]);
57748         this.footerPanel.enableDisplayMode("block");
57749
57750         this.resizeProxy = new E(cs[6]);
57751
57752         this.headerSelector = String.format(
57753            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
57754            this.lockedHd.id, this.mainHd.id
57755         );
57756
57757         this.splitterSelector = String.format(
57758            '#{0} div.x-grid-split, #{1} div.x-grid-split',
57759            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
57760         );
57761     },
57762     idToCssName : function(s)
57763     {
57764         return s.replace(/[^a-z0-9]+/ig, '-');
57765     },
57766
57767     getHeaderCell : function(index){
57768         return Roo.DomQuery.select(this.headerSelector)[index];
57769     },
57770
57771     getHeaderCellMeasure : function(index){
57772         return this.getHeaderCell(index).firstChild;
57773     },
57774
57775     getHeaderCellText : function(index){
57776         return this.getHeaderCell(index).firstChild.firstChild;
57777     },
57778
57779     getLockedTable : function(){
57780         return this.lockedBody.dom.firstChild;
57781     },
57782
57783     getBodyTable : function(){
57784         return this.mainBody.dom.firstChild;
57785     },
57786
57787     getLockedRow : function(index){
57788         return this.getLockedTable().rows[index];
57789     },
57790
57791     getRow : function(index){
57792         return this.getBodyTable().rows[index];
57793     },
57794
57795     getRowComposite : function(index){
57796         if(!this.rowEl){
57797             this.rowEl = new Roo.CompositeElementLite();
57798         }
57799         var els = [], lrow, mrow;
57800         if(lrow = this.getLockedRow(index)){
57801             els.push(lrow);
57802         }
57803         if(mrow = this.getRow(index)){
57804             els.push(mrow);
57805         }
57806         this.rowEl.elements = els;
57807         return this.rowEl;
57808     },
57809     /**
57810      * Gets the 'td' of the cell
57811      * 
57812      * @param {Integer} rowIndex row to select
57813      * @param {Integer} colIndex column to select
57814      * 
57815      * @return {Object} 
57816      */
57817     getCell : function(rowIndex, colIndex){
57818         var locked = this.cm.getLockedCount();
57819         var source;
57820         if(colIndex < locked){
57821             source = this.lockedBody.dom.firstChild;
57822         }else{
57823             source = this.mainBody.dom.firstChild;
57824             colIndex -= locked;
57825         }
57826         return source.rows[rowIndex].childNodes[colIndex];
57827     },
57828
57829     getCellText : function(rowIndex, colIndex){
57830         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
57831     },
57832
57833     getCellBox : function(cell){
57834         var b = this.fly(cell).getBox();
57835         if(Roo.isOpera){ // opera fails to report the Y
57836             b.y = cell.offsetTop + this.mainBody.getY();
57837         }
57838         return b;
57839     },
57840
57841     getCellIndex : function(cell){
57842         var id = String(cell.className).match(this.cellRE);
57843         if(id){
57844             return parseInt(id[1], 10);
57845         }
57846         return 0;
57847     },
57848
57849     findHeaderIndex : function(n){
57850         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57851         return r ? this.getCellIndex(r) : false;
57852     },
57853
57854     findHeaderCell : function(n){
57855         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57856         return r ? r : false;
57857     },
57858
57859     findRowIndex : function(n){
57860         if(!n){
57861             return false;
57862         }
57863         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
57864         return r ? r.rowIndex : false;
57865     },
57866
57867     findCellIndex : function(node){
57868         var stop = this.el.dom;
57869         while(node && node != stop){
57870             if(this.findRE.test(node.className)){
57871                 return this.getCellIndex(node);
57872             }
57873             node = node.parentNode;
57874         }
57875         return false;
57876     },
57877
57878     getColumnId : function(index){
57879         return this.cm.getColumnId(index);
57880     },
57881
57882     getSplitters : function()
57883     {
57884         if(this.splitterSelector){
57885            return Roo.DomQuery.select(this.splitterSelector);
57886         }else{
57887             return null;
57888       }
57889     },
57890
57891     getSplitter : function(index){
57892         return this.getSplitters()[index];
57893     },
57894
57895     onRowOver : function(e, t){
57896         var row;
57897         if((row = this.findRowIndex(t)) !== false){
57898             this.getRowComposite(row).addClass("x-grid-row-over");
57899         }
57900     },
57901
57902     onRowOut : function(e, t){
57903         var row;
57904         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
57905             this.getRowComposite(row).removeClass("x-grid-row-over");
57906         }
57907     },
57908
57909     renderHeaders : function(){
57910         var cm = this.cm;
57911         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
57912         var cb = [], lb = [], sb = [], lsb = [], p = {};
57913         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57914             p.cellId = "x-grid-hd-0-" + i;
57915             p.splitId = "x-grid-csplit-0-" + i;
57916             p.id = cm.getColumnId(i);
57917             p.value = cm.getColumnHeader(i) || "";
57918             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
57919             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
57920             if(!cm.isLocked(i)){
57921                 cb[cb.length] = ct.apply(p);
57922                 sb[sb.length] = st.apply(p);
57923             }else{
57924                 lb[lb.length] = ct.apply(p);
57925                 lsb[lsb.length] = st.apply(p);
57926             }
57927         }
57928         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
57929                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
57930     },
57931
57932     updateHeaders : function(){
57933         var html = this.renderHeaders();
57934         this.lockedHd.update(html[0]);
57935         this.mainHd.update(html[1]);
57936     },
57937
57938     /**
57939      * Focuses the specified row.
57940      * @param {Number} row The row index
57941      */
57942     focusRow : function(row)
57943     {
57944         //Roo.log('GridView.focusRow');
57945         var x = this.scroller.dom.scrollLeft;
57946         this.focusCell(row, 0, false);
57947         this.scroller.dom.scrollLeft = x;
57948     },
57949
57950     /**
57951      * Focuses the specified cell.
57952      * @param {Number} row The row index
57953      * @param {Number} col The column index
57954      * @param {Boolean} hscroll false to disable horizontal scrolling
57955      */
57956     focusCell : function(row, col, hscroll)
57957     {
57958         //Roo.log('GridView.focusCell');
57959         var el = this.ensureVisible(row, col, hscroll);
57960         this.focusEl.alignTo(el, "tl-tl");
57961         if(Roo.isGecko){
57962             this.focusEl.focus();
57963         }else{
57964             this.focusEl.focus.defer(1, this.focusEl);
57965         }
57966     },
57967
57968     /**
57969      * Scrolls the specified cell into view
57970      * @param {Number} row The row index
57971      * @param {Number} col The column index
57972      * @param {Boolean} hscroll false to disable horizontal scrolling
57973      */
57974     ensureVisible : function(row, col, hscroll)
57975     {
57976         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
57977         //return null; //disable for testing.
57978         if(typeof row != "number"){
57979             row = row.rowIndex;
57980         }
57981         if(row < 0 && row >= this.ds.getCount()){
57982             return  null;
57983         }
57984         col = (col !== undefined ? col : 0);
57985         var cm = this.grid.colModel;
57986         while(cm.isHidden(col)){
57987             col++;
57988         }
57989
57990         var el = this.getCell(row, col);
57991         if(!el){
57992             return null;
57993         }
57994         var c = this.scroller.dom;
57995
57996         var ctop = parseInt(el.offsetTop, 10);
57997         var cleft = parseInt(el.offsetLeft, 10);
57998         var cbot = ctop + el.offsetHeight;
57999         var cright = cleft + el.offsetWidth;
58000         
58001         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
58002         var stop = parseInt(c.scrollTop, 10);
58003         var sleft = parseInt(c.scrollLeft, 10);
58004         var sbot = stop + ch;
58005         var sright = sleft + c.clientWidth;
58006         /*
58007         Roo.log('GridView.ensureVisible:' +
58008                 ' ctop:' + ctop +
58009                 ' c.clientHeight:' + c.clientHeight +
58010                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
58011                 ' stop:' + stop +
58012                 ' cbot:' + cbot +
58013                 ' sbot:' + sbot +
58014                 ' ch:' + ch  
58015                 );
58016         */
58017         if(ctop < stop){
58018             c.scrollTop = ctop;
58019             //Roo.log("set scrolltop to ctop DISABLE?");
58020         }else if(cbot > sbot){
58021             //Roo.log("set scrolltop to cbot-ch");
58022             c.scrollTop = cbot-ch;
58023         }
58024         
58025         if(hscroll !== false){
58026             if(cleft < sleft){
58027                 c.scrollLeft = cleft;
58028             }else if(cright > sright){
58029                 c.scrollLeft = cright-c.clientWidth;
58030             }
58031         }
58032          
58033         return el;
58034     },
58035
58036     updateColumns : function(){
58037         this.grid.stopEditing();
58038         var cm = this.grid.colModel, colIds = this.getColumnIds();
58039         //var totalWidth = cm.getTotalWidth();
58040         var pos = 0;
58041         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58042             //if(cm.isHidden(i)) continue;
58043             var w = cm.getColumnWidth(i);
58044             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58045             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58046         }
58047         this.updateSplitters();
58048     },
58049
58050     generateRules : function(cm){
58051         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
58052         Roo.util.CSS.removeStyleSheet(rulesId);
58053         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58054             var cid = cm.getColumnId(i);
58055             var align = '';
58056             if(cm.config[i].align){
58057                 align = 'text-align:'+cm.config[i].align+';';
58058             }
58059             var hidden = '';
58060             if(cm.isHidden(i)){
58061                 hidden = 'display:none;';
58062             }
58063             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
58064             ruleBuf.push(
58065                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
58066                     this.hdSelector, cid, " {\n", align, width, "}\n",
58067                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
58068                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
58069         }
58070         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58071     },
58072
58073     updateSplitters : function(){
58074         var cm = this.cm, s = this.getSplitters();
58075         if(s){ // splitters not created yet
58076             var pos = 0, locked = true;
58077             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58078                 if(cm.isHidden(i)) {
58079                     continue;
58080                 }
58081                 var w = cm.getColumnWidth(i); // make sure it's a number
58082                 if(!cm.isLocked(i) && locked){
58083                     pos = 0;
58084                     locked = false;
58085                 }
58086                 pos += w;
58087                 s[i].style.left = (pos-this.splitOffset) + "px";
58088             }
58089         }
58090     },
58091
58092     handleHiddenChange : function(colModel, colIndex, hidden){
58093         if(hidden){
58094             this.hideColumn(colIndex);
58095         }else{
58096             this.unhideColumn(colIndex);
58097         }
58098     },
58099
58100     hideColumn : function(colIndex){
58101         var cid = this.getColumnId(colIndex);
58102         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
58103         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
58104         if(Roo.isSafari){
58105             this.updateHeaders();
58106         }
58107         this.updateSplitters();
58108         this.layout();
58109     },
58110
58111     unhideColumn : function(colIndex){
58112         var cid = this.getColumnId(colIndex);
58113         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
58114         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
58115
58116         if(Roo.isSafari){
58117             this.updateHeaders();
58118         }
58119         this.updateSplitters();
58120         this.layout();
58121     },
58122
58123     insertRows : function(dm, firstRow, lastRow, isUpdate){
58124         if(firstRow == 0 && lastRow == dm.getCount()-1){
58125             this.refresh();
58126         }else{
58127             if(!isUpdate){
58128                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
58129             }
58130             var s = this.getScrollState();
58131             var markup = this.renderRows(firstRow, lastRow);
58132             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
58133             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
58134             this.restoreScroll(s);
58135             if(!isUpdate){
58136                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
58137                 this.syncRowHeights(firstRow, lastRow);
58138                 this.stripeRows(firstRow);
58139                 this.layout();
58140             }
58141         }
58142     },
58143
58144     bufferRows : function(markup, target, index){
58145         var before = null, trows = target.rows, tbody = target.tBodies[0];
58146         if(index < trows.length){
58147             before = trows[index];
58148         }
58149         var b = document.createElement("div");
58150         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
58151         var rows = b.firstChild.rows;
58152         for(var i = 0, len = rows.length; i < len; i++){
58153             if(before){
58154                 tbody.insertBefore(rows[0], before);
58155             }else{
58156                 tbody.appendChild(rows[0]);
58157             }
58158         }
58159         b.innerHTML = "";
58160         b = null;
58161     },
58162
58163     deleteRows : function(dm, firstRow, lastRow){
58164         if(dm.getRowCount()<1){
58165             this.fireEvent("beforerefresh", this);
58166             this.mainBody.update("");
58167             this.lockedBody.update("");
58168             this.fireEvent("refresh", this);
58169         }else{
58170             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
58171             var bt = this.getBodyTable();
58172             var tbody = bt.firstChild;
58173             var rows = bt.rows;
58174             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
58175                 tbody.removeChild(rows[firstRow]);
58176             }
58177             this.stripeRows(firstRow);
58178             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
58179         }
58180     },
58181
58182     updateRows : function(dataSource, firstRow, lastRow){
58183         var s = this.getScrollState();
58184         this.refresh();
58185         this.restoreScroll(s);
58186     },
58187
58188     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
58189         if(!noRefresh){
58190            this.refresh();
58191         }
58192         this.updateHeaderSortState();
58193     },
58194
58195     getScrollState : function(){
58196         
58197         var sb = this.scroller.dom;
58198         return {left: sb.scrollLeft, top: sb.scrollTop};
58199     },
58200
58201     stripeRows : function(startRow){
58202         if(!this.grid.stripeRows || this.ds.getCount() < 1){
58203             return;
58204         }
58205         startRow = startRow || 0;
58206         var rows = this.getBodyTable().rows;
58207         var lrows = this.getLockedTable().rows;
58208         var cls = ' x-grid-row-alt ';
58209         for(var i = startRow, len = rows.length; i < len; i++){
58210             var row = rows[i], lrow = lrows[i];
58211             var isAlt = ((i+1) % 2 == 0);
58212             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
58213             if(isAlt == hasAlt){
58214                 continue;
58215             }
58216             if(isAlt){
58217                 row.className += " x-grid-row-alt";
58218             }else{
58219                 row.className = row.className.replace("x-grid-row-alt", "");
58220             }
58221             if(lrow){
58222                 lrow.className = row.className;
58223             }
58224         }
58225     },
58226
58227     restoreScroll : function(state){
58228         //Roo.log('GridView.restoreScroll');
58229         var sb = this.scroller.dom;
58230         sb.scrollLeft = state.left;
58231         sb.scrollTop = state.top;
58232         this.syncScroll();
58233     },
58234
58235     syncScroll : function(){
58236         //Roo.log('GridView.syncScroll');
58237         var sb = this.scroller.dom;
58238         var sh = this.mainHd.dom;
58239         var bs = this.mainBody.dom;
58240         var lv = this.lockedBody.dom;
58241         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
58242         lv.scrollTop = bs.scrollTop = sb.scrollTop;
58243     },
58244
58245     handleScroll : function(e){
58246         this.syncScroll();
58247         var sb = this.scroller.dom;
58248         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
58249         e.stopEvent();
58250     },
58251
58252     handleWheel : function(e){
58253         var d = e.getWheelDelta();
58254         this.scroller.dom.scrollTop -= d*22;
58255         // set this here to prevent jumpy scrolling on large tables
58256         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
58257         e.stopEvent();
58258     },
58259
58260     renderRows : function(startRow, endRow){
58261         // pull in all the crap needed to render rows
58262         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
58263         var colCount = cm.getColumnCount();
58264
58265         if(ds.getCount() < 1){
58266             return ["", ""];
58267         }
58268
58269         // build a map for all the columns
58270         var cs = [];
58271         for(var i = 0; i < colCount; i++){
58272             var name = cm.getDataIndex(i);
58273             cs[i] = {
58274                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58275                 renderer : cm.getRenderer(i),
58276                 id : cm.getColumnId(i),
58277                 locked : cm.isLocked(i),
58278                 has_editor : cm.isCellEditable(i)
58279             };
58280         }
58281
58282         startRow = startRow || 0;
58283         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58284
58285         // records to render
58286         var rs = ds.getRange(startRow, endRow);
58287
58288         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58289     },
58290
58291     // As much as I hate to duplicate code, this was branched because FireFox really hates
58292     // [].join("") on strings. The performance difference was substantial enough to
58293     // branch this function
58294     doRender : Roo.isGecko ?
58295             function(cs, rs, ds, startRow, colCount, stripe){
58296                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58297                 // buffers
58298                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58299                 
58300                 var hasListener = this.grid.hasListener('rowclass');
58301                 var rowcfg = {};
58302                 for(var j = 0, len = rs.length; j < len; j++){
58303                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58304                     for(var i = 0; i < colCount; i++){
58305                         c = cs[i];
58306                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58307                         p.id = c.id;
58308                         p.css = p.attr = "";
58309                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58310                         if(p.value == undefined || p.value === "") {
58311                             p.value = "&#160;";
58312                         }
58313                         if(c.has_editor){
58314                             p.css += ' x-grid-editable-cell';
58315                         }
58316                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58317                             p.css +=  ' x-grid-dirty-cell';
58318                         }
58319                         var markup = ct.apply(p);
58320                         if(!c.locked){
58321                             cb+= markup;
58322                         }else{
58323                             lcb+= markup;
58324                         }
58325                     }
58326                     var alt = [];
58327                     if(stripe && ((rowIndex+1) % 2 == 0)){
58328                         alt.push("x-grid-row-alt")
58329                     }
58330                     if(r.dirty){
58331                         alt.push(  " x-grid-dirty-row");
58332                     }
58333                     rp.cells = lcb;
58334                     if(this.getRowClass){
58335                         alt.push(this.getRowClass(r, rowIndex));
58336                     }
58337                     if (hasListener) {
58338                         rowcfg = {
58339                              
58340                             record: r,
58341                             rowIndex : rowIndex,
58342                             rowClass : ''
58343                         };
58344                         this.grid.fireEvent('rowclass', this, rowcfg);
58345                         alt.push(rowcfg.rowClass);
58346                     }
58347                     rp.alt = alt.join(" ");
58348                     lbuf+= rt.apply(rp);
58349                     rp.cells = cb;
58350                     buf+=  rt.apply(rp);
58351                 }
58352                 return [lbuf, buf];
58353             } :
58354             function(cs, rs, ds, startRow, colCount, stripe){
58355                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58356                 // buffers
58357                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58358                 var hasListener = this.grid.hasListener('rowclass');
58359  
58360                 var rowcfg = {};
58361                 for(var j = 0, len = rs.length; j < len; j++){
58362                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
58363                     for(var i = 0; i < colCount; i++){
58364                         c = cs[i];
58365                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58366                         p.id = c.id;
58367                         p.css = p.attr = "";
58368                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58369                         if(p.value == undefined || p.value === "") {
58370                             p.value = "&#160;";
58371                         }
58372                         //Roo.log(c);
58373                          if(c.has_editor){
58374                             p.css += ' x-grid-editable-cell';
58375                         }
58376                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
58377                             p.css += ' x-grid-dirty-cell' 
58378                         }
58379                         
58380                         var markup = ct.apply(p);
58381                         if(!c.locked){
58382                             cb[cb.length] = markup;
58383                         }else{
58384                             lcb[lcb.length] = markup;
58385                         }
58386                     }
58387                     var alt = [];
58388                     if(stripe && ((rowIndex+1) % 2 == 0)){
58389                         alt.push( "x-grid-row-alt");
58390                     }
58391                     if(r.dirty){
58392                         alt.push(" x-grid-dirty-row");
58393                     }
58394                     rp.cells = lcb;
58395                     if(this.getRowClass){
58396                         alt.push( this.getRowClass(r, rowIndex));
58397                     }
58398                     if (hasListener) {
58399                         rowcfg = {
58400                              
58401                             record: r,
58402                             rowIndex : rowIndex,
58403                             rowClass : ''
58404                         };
58405                         this.grid.fireEvent('rowclass', this, rowcfg);
58406                         alt.push(rowcfg.rowClass);
58407                     }
58408                     
58409                     rp.alt = alt.join(" ");
58410                     rp.cells = lcb.join("");
58411                     lbuf[lbuf.length] = rt.apply(rp);
58412                     rp.cells = cb.join("");
58413                     buf[buf.length] =  rt.apply(rp);
58414                 }
58415                 return [lbuf.join(""), buf.join("")];
58416             },
58417
58418     renderBody : function(){
58419         var markup = this.renderRows();
58420         var bt = this.templates.body;
58421         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
58422     },
58423
58424     /**
58425      * Refreshes the grid
58426      * @param {Boolean} headersToo
58427      */
58428     refresh : function(headersToo){
58429         this.fireEvent("beforerefresh", this);
58430         this.grid.stopEditing();
58431         var result = this.renderBody();
58432         this.lockedBody.update(result[0]);
58433         this.mainBody.update(result[1]);
58434         if(headersToo === true){
58435             this.updateHeaders();
58436             this.updateColumns();
58437             this.updateSplitters();
58438             this.updateHeaderSortState();
58439         }
58440         this.syncRowHeights();
58441         this.layout();
58442         this.fireEvent("refresh", this);
58443     },
58444
58445     handleColumnMove : function(cm, oldIndex, newIndex){
58446         this.indexMap = null;
58447         var s = this.getScrollState();
58448         this.refresh(true);
58449         this.restoreScroll(s);
58450         this.afterMove(newIndex);
58451     },
58452
58453     afterMove : function(colIndex){
58454         if(this.enableMoveAnim && Roo.enableFx){
58455             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
58456         }
58457         // if multisort - fix sortOrder, and reload..
58458         if (this.grid.dataSource.multiSort) {
58459             // the we can call sort again..
58460             var dm = this.grid.dataSource;
58461             var cm = this.grid.colModel;
58462             var so = [];
58463             for(var i = 0; i < cm.config.length; i++ ) {
58464                 
58465                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
58466                     continue; // dont' bother, it's not in sort list or being set.
58467                 }
58468                 
58469                 so.push(cm.config[i].dataIndex);
58470             };
58471             dm.sortOrder = so;
58472             dm.load(dm.lastOptions);
58473             
58474             
58475         }
58476         
58477     },
58478
58479     updateCell : function(dm, rowIndex, dataIndex){
58480         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
58481         if(typeof colIndex == "undefined"){ // not present in grid
58482             return;
58483         }
58484         var cm = this.grid.colModel;
58485         var cell = this.getCell(rowIndex, colIndex);
58486         var cellText = this.getCellText(rowIndex, colIndex);
58487
58488         var p = {
58489             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
58490             id : cm.getColumnId(colIndex),
58491             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
58492         };
58493         var renderer = cm.getRenderer(colIndex);
58494         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
58495         if(typeof val == "undefined" || val === "") {
58496             val = "&#160;";
58497         }
58498         cellText.innerHTML = val;
58499         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
58500         this.syncRowHeights(rowIndex, rowIndex);
58501     },
58502
58503     calcColumnWidth : function(colIndex, maxRowsToMeasure){
58504         var maxWidth = 0;
58505         if(this.grid.autoSizeHeaders){
58506             var h = this.getHeaderCellMeasure(colIndex);
58507             maxWidth = Math.max(maxWidth, h.scrollWidth);
58508         }
58509         var tb, index;
58510         if(this.cm.isLocked(colIndex)){
58511             tb = this.getLockedTable();
58512             index = colIndex;
58513         }else{
58514             tb = this.getBodyTable();
58515             index = colIndex - this.cm.getLockedCount();
58516         }
58517         if(tb && tb.rows){
58518             var rows = tb.rows;
58519             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
58520             for(var i = 0; i < stopIndex; i++){
58521                 var cell = rows[i].childNodes[index].firstChild;
58522                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
58523             }
58524         }
58525         return maxWidth + /*margin for error in IE*/ 5;
58526     },
58527     /**
58528      * Autofit a column to its content.
58529      * @param {Number} colIndex
58530      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
58531      */
58532      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
58533          if(this.cm.isHidden(colIndex)){
58534              return; // can't calc a hidden column
58535          }
58536         if(forceMinSize){
58537             var cid = this.cm.getColumnId(colIndex);
58538             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
58539            if(this.grid.autoSizeHeaders){
58540                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
58541            }
58542         }
58543         var newWidth = this.calcColumnWidth(colIndex);
58544         this.cm.setColumnWidth(colIndex,
58545             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
58546         if(!suppressEvent){
58547             this.grid.fireEvent("columnresize", colIndex, newWidth);
58548         }
58549     },
58550
58551     /**
58552      * Autofits all columns to their content and then expands to fit any extra space in the grid
58553      */
58554      autoSizeColumns : function(){
58555         var cm = this.grid.colModel;
58556         var colCount = cm.getColumnCount();
58557         for(var i = 0; i < colCount; i++){
58558             this.autoSizeColumn(i, true, true);
58559         }
58560         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
58561             this.fitColumns();
58562         }else{
58563             this.updateColumns();
58564             this.layout();
58565         }
58566     },
58567
58568     /**
58569      * Autofits all columns to the grid's width proportionate with their current size
58570      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
58571      */
58572     fitColumns : function(reserveScrollSpace){
58573         var cm = this.grid.colModel;
58574         var colCount = cm.getColumnCount();
58575         var cols = [];
58576         var width = 0;
58577         var i, w;
58578         for (i = 0; i < colCount; i++){
58579             if(!cm.isHidden(i) && !cm.isFixed(i)){
58580                 w = cm.getColumnWidth(i);
58581                 cols.push(i);
58582                 cols.push(w);
58583                 width += w;
58584             }
58585         }
58586         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
58587         if(reserveScrollSpace){
58588             avail -= 17;
58589         }
58590         var frac = (avail - cm.getTotalWidth())/width;
58591         while (cols.length){
58592             w = cols.pop();
58593             i = cols.pop();
58594             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
58595         }
58596         this.updateColumns();
58597         this.layout();
58598     },
58599
58600     onRowSelect : function(rowIndex){
58601         var row = this.getRowComposite(rowIndex);
58602         row.addClass("x-grid-row-selected");
58603     },
58604
58605     onRowDeselect : function(rowIndex){
58606         var row = this.getRowComposite(rowIndex);
58607         row.removeClass("x-grid-row-selected");
58608     },
58609
58610     onCellSelect : function(row, col){
58611         var cell = this.getCell(row, col);
58612         if(cell){
58613             Roo.fly(cell).addClass("x-grid-cell-selected");
58614         }
58615     },
58616
58617     onCellDeselect : function(row, col){
58618         var cell = this.getCell(row, col);
58619         if(cell){
58620             Roo.fly(cell).removeClass("x-grid-cell-selected");
58621         }
58622     },
58623
58624     updateHeaderSortState : function(){
58625         
58626         // sort state can be single { field: xxx, direction : yyy}
58627         // or   { xxx=>ASC , yyy : DESC ..... }
58628         
58629         var mstate = {};
58630         if (!this.ds.multiSort) { 
58631             var state = this.ds.getSortState();
58632             if(!state){
58633                 return;
58634             }
58635             mstate[state.field] = state.direction;
58636             // FIXME... - this is not used here.. but might be elsewhere..
58637             this.sortState = state;
58638             
58639         } else {
58640             mstate = this.ds.sortToggle;
58641         }
58642         //remove existing sort classes..
58643         
58644         var sc = this.sortClasses;
58645         var hds = this.el.select(this.headerSelector).removeClass(sc);
58646         
58647         for(var f in mstate) {
58648         
58649             var sortColumn = this.cm.findColumnIndex(f);
58650             
58651             if(sortColumn != -1){
58652                 var sortDir = mstate[f];        
58653                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
58654             }
58655         }
58656         
58657          
58658         
58659     },
58660
58661
58662     handleHeaderClick : function(g, index,e){
58663         
58664         Roo.log("header click");
58665         
58666         if (Roo.isTouch) {
58667             // touch events on header are handled by context
58668             this.handleHdCtx(g,index,e);
58669             return;
58670         }
58671         
58672         
58673         if(this.headersDisabled){
58674             return;
58675         }
58676         var dm = g.dataSource, cm = g.colModel;
58677         if(!cm.isSortable(index)){
58678             return;
58679         }
58680         g.stopEditing();
58681         
58682         if (dm.multiSort) {
58683             // update the sortOrder
58684             var so = [];
58685             for(var i = 0; i < cm.config.length; i++ ) {
58686                 
58687                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
58688                     continue; // dont' bother, it's not in sort list or being set.
58689                 }
58690                 
58691                 so.push(cm.config[i].dataIndex);
58692             };
58693             dm.sortOrder = so;
58694         }
58695         
58696         
58697         dm.sort(cm.getDataIndex(index));
58698     },
58699
58700
58701     destroy : function(){
58702         if(this.colMenu){
58703             this.colMenu.removeAll();
58704             Roo.menu.MenuMgr.unregister(this.colMenu);
58705             this.colMenu.getEl().remove();
58706             delete this.colMenu;
58707         }
58708         if(this.hmenu){
58709             this.hmenu.removeAll();
58710             Roo.menu.MenuMgr.unregister(this.hmenu);
58711             this.hmenu.getEl().remove();
58712             delete this.hmenu;
58713         }
58714         if(this.grid.enableColumnMove){
58715             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58716             if(dds){
58717                 for(var dd in dds){
58718                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
58719                         var elid = dds[dd].dragElId;
58720                         dds[dd].unreg();
58721                         Roo.get(elid).remove();
58722                     } else if(dds[dd].config.isTarget){
58723                         dds[dd].proxyTop.remove();
58724                         dds[dd].proxyBottom.remove();
58725                         dds[dd].unreg();
58726                     }
58727                     if(Roo.dd.DDM.locationCache[dd]){
58728                         delete Roo.dd.DDM.locationCache[dd];
58729                     }
58730                 }
58731                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58732             }
58733         }
58734         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
58735         this.bind(null, null);
58736         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
58737     },
58738
58739     handleLockChange : function(){
58740         this.refresh(true);
58741     },
58742
58743     onDenyColumnLock : function(){
58744
58745     },
58746
58747     onDenyColumnHide : function(){
58748
58749     },
58750
58751     handleHdMenuClick : function(item){
58752         var index = this.hdCtxIndex;
58753         var cm = this.cm, ds = this.ds;
58754         switch(item.id){
58755             case "asc":
58756                 ds.sort(cm.getDataIndex(index), "ASC");
58757                 break;
58758             case "desc":
58759                 ds.sort(cm.getDataIndex(index), "DESC");
58760                 break;
58761             case "lock":
58762                 var lc = cm.getLockedCount();
58763                 if(cm.getColumnCount(true) <= lc+1){
58764                     this.onDenyColumnLock();
58765                     return;
58766                 }
58767                 if(lc != index){
58768                     cm.setLocked(index, true, true);
58769                     cm.moveColumn(index, lc);
58770                     this.grid.fireEvent("columnmove", index, lc);
58771                 }else{
58772                     cm.setLocked(index, true);
58773                 }
58774             break;
58775             case "unlock":
58776                 var lc = cm.getLockedCount();
58777                 if((lc-1) != index){
58778                     cm.setLocked(index, false, true);
58779                     cm.moveColumn(index, lc-1);
58780                     this.grid.fireEvent("columnmove", index, lc-1);
58781                 }else{
58782                     cm.setLocked(index, false);
58783                 }
58784             break;
58785             case 'wider': // used to expand cols on touch..
58786             case 'narrow':
58787                 var cw = cm.getColumnWidth(index);
58788                 cw += (item.id == 'wider' ? 1 : -1) * 50;
58789                 cw = Math.max(0, cw);
58790                 cw = Math.min(cw,4000);
58791                 cm.setColumnWidth(index, cw);
58792                 break;
58793                 
58794             default:
58795                 index = cm.getIndexById(item.id.substr(4));
58796                 if(index != -1){
58797                     if(item.checked && cm.getColumnCount(true) <= 1){
58798                         this.onDenyColumnHide();
58799                         return false;
58800                     }
58801                     cm.setHidden(index, item.checked);
58802                 }
58803         }
58804         return true;
58805     },
58806
58807     beforeColMenuShow : function(){
58808         var cm = this.cm,  colCount = cm.getColumnCount();
58809         this.colMenu.removeAll();
58810         for(var i = 0; i < colCount; i++){
58811             this.colMenu.add(new Roo.menu.CheckItem({
58812                 id: "col-"+cm.getColumnId(i),
58813                 text: cm.getColumnHeader(i),
58814                 checked: !cm.isHidden(i),
58815                 hideOnClick:false
58816             }));
58817         }
58818     },
58819
58820     handleHdCtx : function(g, index, e){
58821         e.stopEvent();
58822         var hd = this.getHeaderCell(index);
58823         this.hdCtxIndex = index;
58824         var ms = this.hmenu.items, cm = this.cm;
58825         ms.get("asc").setDisabled(!cm.isSortable(index));
58826         ms.get("desc").setDisabled(!cm.isSortable(index));
58827         if(this.grid.enableColLock !== false){
58828             ms.get("lock").setDisabled(cm.isLocked(index));
58829             ms.get("unlock").setDisabled(!cm.isLocked(index));
58830         }
58831         this.hmenu.show(hd, "tl-bl");
58832     },
58833
58834     handleHdOver : function(e){
58835         var hd = this.findHeaderCell(e.getTarget());
58836         if(hd && !this.headersDisabled){
58837             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
58838                this.fly(hd).addClass("x-grid-hd-over");
58839             }
58840         }
58841     },
58842
58843     handleHdOut : function(e){
58844         var hd = this.findHeaderCell(e.getTarget());
58845         if(hd){
58846             this.fly(hd).removeClass("x-grid-hd-over");
58847         }
58848     },
58849
58850     handleSplitDblClick : function(e, t){
58851         var i = this.getCellIndex(t);
58852         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
58853             this.autoSizeColumn(i, true);
58854             this.layout();
58855         }
58856     },
58857
58858     render : function(){
58859
58860         var cm = this.cm;
58861         var colCount = cm.getColumnCount();
58862
58863         if(this.grid.monitorWindowResize === true){
58864             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58865         }
58866         var header = this.renderHeaders();
58867         var body = this.templates.body.apply({rows:""});
58868         var html = this.templates.master.apply({
58869             lockedBody: body,
58870             body: body,
58871             lockedHeader: header[0],
58872             header: header[1]
58873         });
58874
58875         //this.updateColumns();
58876
58877         this.grid.getGridEl().dom.innerHTML = html;
58878
58879         this.initElements();
58880         
58881         // a kludge to fix the random scolling effect in webkit
58882         this.el.on("scroll", function() {
58883             this.el.dom.scrollTop=0; // hopefully not recursive..
58884         },this);
58885
58886         this.scroller.on("scroll", this.handleScroll, this);
58887         this.lockedBody.on("mousewheel", this.handleWheel, this);
58888         this.mainBody.on("mousewheel", this.handleWheel, this);
58889
58890         this.mainHd.on("mouseover", this.handleHdOver, this);
58891         this.mainHd.on("mouseout", this.handleHdOut, this);
58892         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
58893                 {delegate: "."+this.splitClass});
58894
58895         this.lockedHd.on("mouseover", this.handleHdOver, this);
58896         this.lockedHd.on("mouseout", this.handleHdOut, this);
58897         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
58898                 {delegate: "."+this.splitClass});
58899
58900         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
58901             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58902         }
58903
58904         this.updateSplitters();
58905
58906         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
58907             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58908             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58909         }
58910
58911         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
58912             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
58913             this.hmenu.add(
58914                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
58915                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
58916             );
58917             if(this.grid.enableColLock !== false){
58918                 this.hmenu.add('-',
58919                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
58920                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
58921                 );
58922             }
58923             if (Roo.isTouch) {
58924                  this.hmenu.add('-',
58925                     {id:"wider", text: this.columnsWiderText},
58926                     {id:"narrow", text: this.columnsNarrowText }
58927                 );
58928                 
58929                  
58930             }
58931             
58932             if(this.grid.enableColumnHide !== false){
58933
58934                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
58935                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
58936                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
58937
58938                 this.hmenu.add('-',
58939                     {id:"columns", text: this.columnsText, menu: this.colMenu}
58940                 );
58941             }
58942             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
58943
58944             this.grid.on("headercontextmenu", this.handleHdCtx, this);
58945         }
58946
58947         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
58948             this.dd = new Roo.grid.GridDragZone(this.grid, {
58949                 ddGroup : this.grid.ddGroup || 'GridDD'
58950             });
58951             
58952         }
58953
58954         /*
58955         for(var i = 0; i < colCount; i++){
58956             if(cm.isHidden(i)){
58957                 this.hideColumn(i);
58958             }
58959             if(cm.config[i].align){
58960                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
58961                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
58962             }
58963         }*/
58964         
58965         this.updateHeaderSortState();
58966
58967         this.beforeInitialResize();
58968         this.layout(true);
58969
58970         // two part rendering gives faster view to the user
58971         this.renderPhase2.defer(1, this);
58972     },
58973
58974     renderPhase2 : function(){
58975         // render the rows now
58976         this.refresh();
58977         if(this.grid.autoSizeColumns){
58978             this.autoSizeColumns();
58979         }
58980     },
58981
58982     beforeInitialResize : function(){
58983
58984     },
58985
58986     onColumnSplitterMoved : function(i, w){
58987         this.userResized = true;
58988         var cm = this.grid.colModel;
58989         cm.setColumnWidth(i, w, true);
58990         var cid = cm.getColumnId(i);
58991         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58992         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58993         this.updateSplitters();
58994         this.layout();
58995         this.grid.fireEvent("columnresize", i, w);
58996     },
58997
58998     syncRowHeights : function(startIndex, endIndex){
58999         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
59000             startIndex = startIndex || 0;
59001             var mrows = this.getBodyTable().rows;
59002             var lrows = this.getLockedTable().rows;
59003             var len = mrows.length-1;
59004             endIndex = Math.min(endIndex || len, len);
59005             for(var i = startIndex; i <= endIndex; i++){
59006                 var m = mrows[i], l = lrows[i];
59007                 var h = Math.max(m.offsetHeight, l.offsetHeight);
59008                 m.style.height = l.style.height = h + "px";
59009             }
59010         }
59011     },
59012
59013     layout : function(initialRender, is2ndPass)
59014     {
59015         var g = this.grid;
59016         var auto = g.autoHeight;
59017         var scrollOffset = 16;
59018         var c = g.getGridEl(), cm = this.cm,
59019                 expandCol = g.autoExpandColumn,
59020                 gv = this;
59021         //c.beginMeasure();
59022
59023         if(!c.dom.offsetWidth){ // display:none?
59024             if(initialRender){
59025                 this.lockedWrap.show();
59026                 this.mainWrap.show();
59027             }
59028             return;
59029         }
59030
59031         var hasLock = this.cm.isLocked(0);
59032
59033         var tbh = this.headerPanel.getHeight();
59034         var bbh = this.footerPanel.getHeight();
59035
59036         if(auto){
59037             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
59038             var newHeight = ch + c.getBorderWidth("tb");
59039             if(g.maxHeight){
59040                 newHeight = Math.min(g.maxHeight, newHeight);
59041             }
59042             c.setHeight(newHeight);
59043         }
59044
59045         if(g.autoWidth){
59046             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
59047         }
59048
59049         var s = this.scroller;
59050
59051         var csize = c.getSize(true);
59052
59053         this.el.setSize(csize.width, csize.height);
59054
59055         this.headerPanel.setWidth(csize.width);
59056         this.footerPanel.setWidth(csize.width);
59057
59058         var hdHeight = this.mainHd.getHeight();
59059         var vw = csize.width;
59060         var vh = csize.height - (tbh + bbh);
59061
59062         s.setSize(vw, vh);
59063
59064         var bt = this.getBodyTable();
59065         
59066         if(cm.getLockedCount() == cm.config.length){
59067             bt = this.getLockedTable();
59068         }
59069         
59070         var ltWidth = hasLock ?
59071                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
59072
59073         var scrollHeight = bt.offsetHeight;
59074         var scrollWidth = ltWidth + bt.offsetWidth;
59075         var vscroll = false, hscroll = false;
59076
59077         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
59078
59079         var lw = this.lockedWrap, mw = this.mainWrap;
59080         var lb = this.lockedBody, mb = this.mainBody;
59081
59082         setTimeout(function(){
59083             var t = s.dom.offsetTop;
59084             var w = s.dom.clientWidth,
59085                 h = s.dom.clientHeight;
59086
59087             lw.setTop(t);
59088             lw.setSize(ltWidth, h);
59089
59090             mw.setLeftTop(ltWidth, t);
59091             mw.setSize(w-ltWidth, h);
59092
59093             lb.setHeight(h-hdHeight);
59094             mb.setHeight(h-hdHeight);
59095
59096             if(is2ndPass !== true && !gv.userResized && expandCol){
59097                 // high speed resize without full column calculation
59098                 
59099                 var ci = cm.getIndexById(expandCol);
59100                 if (ci < 0) {
59101                     ci = cm.findColumnIndex(expandCol);
59102                 }
59103                 ci = Math.max(0, ci); // make sure it's got at least the first col.
59104                 var expandId = cm.getColumnId(ci);
59105                 var  tw = cm.getTotalWidth(false);
59106                 var currentWidth = cm.getColumnWidth(ci);
59107                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
59108                 if(currentWidth != cw){
59109                     cm.setColumnWidth(ci, cw, true);
59110                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59111                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59112                     gv.updateSplitters();
59113                     gv.layout(false, true);
59114                 }
59115             }
59116
59117             if(initialRender){
59118                 lw.show();
59119                 mw.show();
59120             }
59121             //c.endMeasure();
59122         }, 10);
59123     },
59124
59125     onWindowResize : function(){
59126         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
59127             return;
59128         }
59129         this.layout();
59130     },
59131
59132     appendFooter : function(parentEl){
59133         return null;
59134     },
59135
59136     sortAscText : "Sort Ascending",
59137     sortDescText : "Sort Descending",
59138     lockText : "Lock Column",
59139     unlockText : "Unlock Column",
59140     columnsText : "Columns",
59141  
59142     columnsWiderText : "Wider",
59143     columnsNarrowText : "Thinner"
59144 });
59145
59146
59147 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
59148     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
59149     this.proxy.el.addClass('x-grid3-col-dd');
59150 };
59151
59152 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
59153     handleMouseDown : function(e){
59154
59155     },
59156
59157     callHandleMouseDown : function(e){
59158         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
59159     }
59160 });
59161 /*
59162  * Based on:
59163  * Ext JS Library 1.1.1
59164  * Copyright(c) 2006-2007, Ext JS, LLC.
59165  *
59166  * Originally Released Under LGPL - original licence link has changed is not relivant.
59167  *
59168  * Fork - LGPL
59169  * <script type="text/javascript">
59170  */
59171  /**
59172  * @extends Roo.dd.DDProxy
59173  * @class Roo.grid.SplitDragZone
59174  * Support for Column Header resizing
59175  * @constructor
59176  * @param {Object} config
59177  */
59178 // private
59179 // This is a support class used internally by the Grid components
59180 Roo.grid.SplitDragZone = function(grid, hd, hd2){
59181     this.grid = grid;
59182     this.view = grid.getView();
59183     this.proxy = this.view.resizeProxy;
59184     Roo.grid.SplitDragZone.superclass.constructor.call(
59185         this,
59186         hd, // ID
59187         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
59188         {  // CONFIG
59189             dragElId : Roo.id(this.proxy.dom),
59190             resizeFrame:false
59191         }
59192     );
59193     
59194     this.setHandleElId(Roo.id(hd));
59195     if (hd2 !== false) {
59196         this.setOuterHandleElId(Roo.id(hd2));
59197     }
59198     
59199     this.scroll = false;
59200 };
59201 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
59202     fly: Roo.Element.fly,
59203
59204     b4StartDrag : function(x, y){
59205         this.view.headersDisabled = true;
59206         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
59207                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
59208         );
59209         this.proxy.setHeight(h);
59210         
59211         // for old system colWidth really stored the actual width?
59212         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
59213         // which in reality did not work.. - it worked only for fixed sizes
59214         // for resizable we need to use actual sizes.
59215         var w = this.cm.getColumnWidth(this.cellIndex);
59216         if (!this.view.mainWrap) {
59217             // bootstrap.
59218             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
59219         }
59220         
59221         
59222         
59223         // this was w-this.grid.minColumnWidth;
59224         // doesnt really make sense? - w = thie curren width or the rendered one?
59225         var minw = Math.max(w-this.grid.minColumnWidth, 0);
59226         this.resetConstraints();
59227         this.setXConstraint(minw, 1000);
59228         this.setYConstraint(0, 0);
59229         this.minX = x - minw;
59230         this.maxX = x + 1000;
59231         this.startPos = x;
59232         if (!this.view.mainWrap) { // this is Bootstrap code..
59233             this.getDragEl().style.display='block';
59234         }
59235         
59236         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
59237     },
59238
59239
59240     handleMouseDown : function(e){
59241         ev = Roo.EventObject.setEvent(e);
59242         var t = this.fly(ev.getTarget());
59243         if(t.hasClass("x-grid-split")){
59244             this.cellIndex = this.view.getCellIndex(t.dom);
59245             this.split = t.dom;
59246             this.cm = this.grid.colModel;
59247             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
59248                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
59249             }
59250         }
59251     },
59252
59253     endDrag : function(e){
59254         this.view.headersDisabled = false;
59255         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
59256         var diff = endX - this.startPos;
59257         // 
59258         var w = this.cm.getColumnWidth(this.cellIndex);
59259         if (!this.view.mainWrap) {
59260             w = 0;
59261         }
59262         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
59263     },
59264
59265     autoOffset : function(){
59266         this.setDelta(0,0);
59267     }
59268 });/*
59269  * Based on:
59270  * Ext JS Library 1.1.1
59271  * Copyright(c) 2006-2007, Ext JS, LLC.
59272  *
59273  * Originally Released Under LGPL - original licence link has changed is not relivant.
59274  *
59275  * Fork - LGPL
59276  * <script type="text/javascript">
59277  */
59278  
59279 // private
59280 // This is a support class used internally by the Grid components
59281 Roo.grid.GridDragZone = function(grid, config){
59282     this.view = grid.getView();
59283     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59284     if(this.view.lockedBody){
59285         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59286         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59287     }
59288     this.scroll = false;
59289     this.grid = grid;
59290     this.ddel = document.createElement('div');
59291     this.ddel.className = 'x-grid-dd-wrap';
59292 };
59293
59294 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59295     ddGroup : "GridDD",
59296
59297     getDragData : function(e){
59298         var t = Roo.lib.Event.getTarget(e);
59299         var rowIndex = this.view.findRowIndex(t);
59300         var sm = this.grid.selModel;
59301             
59302         //Roo.log(rowIndex);
59303         
59304         if (sm.getSelectedCell) {
59305             // cell selection..
59306             if (!sm.getSelectedCell()) {
59307                 return false;
59308             }
59309             if (rowIndex != sm.getSelectedCell()[0]) {
59310                 return false;
59311             }
59312         
59313         }
59314         if (sm.getSelections && sm.getSelections().length < 1) {
59315             return false;
59316         }
59317         
59318         
59319         // before it used to all dragging of unseleted... - now we dont do that.
59320         if(rowIndex !== false){
59321             
59322             // if editorgrid.. 
59323             
59324             
59325             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59326                
59327             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59328               //  
59329             //}
59330             if (e.hasModifier()){
59331                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59332             }
59333             
59334             Roo.log("getDragData");
59335             
59336             return {
59337                 grid: this.grid,
59338                 ddel: this.ddel,
59339                 rowIndex: rowIndex,
59340                 selections: sm.getSelections ? sm.getSelections() : (
59341                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59342             };
59343         }
59344         return false;
59345     },
59346     
59347     
59348     onInitDrag : function(e){
59349         var data = this.dragData;
59350         this.ddel.innerHTML = this.grid.getDragDropText();
59351         this.proxy.update(this.ddel);
59352         // fire start drag?
59353     },
59354
59355     afterRepair : function(){
59356         this.dragging = false;
59357     },
59358
59359     getRepairXY : function(e, data){
59360         return false;
59361     },
59362
59363     onEndDrag : function(data, e){
59364         // fire end drag?
59365     },
59366
59367     onValidDrop : function(dd, e, id){
59368         // fire drag drop?
59369         this.hideProxy();
59370     },
59371
59372     beforeInvalidDrop : function(e, id){
59373
59374     }
59375 });/*
59376  * Based on:
59377  * Ext JS Library 1.1.1
59378  * Copyright(c) 2006-2007, Ext JS, LLC.
59379  *
59380  * Originally Released Under LGPL - original licence link has changed is not relivant.
59381  *
59382  * Fork - LGPL
59383  * <script type="text/javascript">
59384  */
59385  
59386
59387 /**
59388  * @class Roo.grid.ColumnModel
59389  * @extends Roo.util.Observable
59390  * This is the default implementation of a ColumnModel used by the Grid. It defines
59391  * the columns in the grid.
59392  * <br>Usage:<br>
59393  <pre><code>
59394  var colModel = new Roo.grid.ColumnModel([
59395         {header: "Ticker", width: 60, sortable: true, locked: true},
59396         {header: "Company Name", width: 150, sortable: true},
59397         {header: "Market Cap.", width: 100, sortable: true},
59398         {header: "$ Sales", width: 100, sortable: true, renderer: money},
59399         {header: "Employees", width: 100, sortable: true, resizable: false}
59400  ]);
59401  </code></pre>
59402  * <p>
59403  
59404  * The config options listed for this class are options which may appear in each
59405  * individual column definition.
59406  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
59407  * @constructor
59408  * @param {Object} config An Array of column config objects. See this class's
59409  * config objects for details.
59410 */
59411 Roo.grid.ColumnModel = function(config){
59412         /**
59413      * The config passed into the constructor
59414      */
59415     this.config = []; //config;
59416     this.lookup = {};
59417
59418     // if no id, create one
59419     // if the column does not have a dataIndex mapping,
59420     // map it to the order it is in the config
59421     for(var i = 0, len = config.length; i < len; i++){
59422         this.addColumn(config[i]);
59423         
59424     }
59425
59426     /**
59427      * The width of columns which have no width specified (defaults to 100)
59428      * @type Number
59429      */
59430     this.defaultWidth = 100;
59431
59432     /**
59433      * Default sortable of columns which have no sortable specified (defaults to false)
59434      * @type Boolean
59435      */
59436     this.defaultSortable = false;
59437
59438     this.addEvents({
59439         /**
59440              * @event widthchange
59441              * Fires when the width of a column changes.
59442              * @param {ColumnModel} this
59443              * @param {Number} columnIndex The column index
59444              * @param {Number} newWidth The new width
59445              */
59446             "widthchange": true,
59447         /**
59448              * @event headerchange
59449              * Fires when the text of a header changes.
59450              * @param {ColumnModel} this
59451              * @param {Number} columnIndex The column index
59452              * @param {Number} newText The new header text
59453              */
59454             "headerchange": true,
59455         /**
59456              * @event hiddenchange
59457              * Fires when a column is hidden or "unhidden".
59458              * @param {ColumnModel} this
59459              * @param {Number} columnIndex The column index
59460              * @param {Boolean} hidden true if hidden, false otherwise
59461              */
59462             "hiddenchange": true,
59463             /**
59464          * @event columnmoved
59465          * Fires when a column is moved.
59466          * @param {ColumnModel} this
59467          * @param {Number} oldIndex
59468          * @param {Number} newIndex
59469          */
59470         "columnmoved" : true,
59471         /**
59472          * @event columlockchange
59473          * Fires when a column's locked state is changed
59474          * @param {ColumnModel} this
59475          * @param {Number} colIndex
59476          * @param {Boolean} locked true if locked
59477          */
59478         "columnlockchange" : true
59479     });
59480     Roo.grid.ColumnModel.superclass.constructor.call(this);
59481 };
59482 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
59483     /**
59484      * @cfg {String} header The header text to display in the Grid view.
59485      */
59486         /**
59487      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
59488      */
59489         /**
59490      * @cfg {String} smHeader Header at Bootsrap Small width
59491      */
59492         /**
59493      * @cfg {String} mdHeader Header at Bootsrap Medium width
59494      */
59495         /**
59496      * @cfg {String} lgHeader Header at Bootsrap Large width
59497      */
59498         /**
59499      * @cfg {String} xlHeader Header at Bootsrap extra Large width
59500      */
59501     /**
59502      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
59503      * {@link Roo.data.Record} definition from which to draw the column's value. If not
59504      * specified, the column's index is used as an index into the Record's data Array.
59505      */
59506     /**
59507      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
59508      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
59509      */
59510     /**
59511      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
59512      * Defaults to the value of the {@link #defaultSortable} property.
59513      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
59514      */
59515     /**
59516      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
59517      */
59518     /**
59519      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
59520      */
59521     /**
59522      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
59523      */
59524     /**
59525      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
59526      */
59527     /**
59528      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
59529      * given the cell's data value. See {@link #setRenderer}. If not specified, the
59530      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
59531      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
59532      */
59533        /**
59534      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
59535      */
59536     /**
59537      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
59538      */
59539     /**
59540      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
59541      */
59542     /**
59543      * @cfg {String} cursor (Optional)
59544      */
59545     /**
59546      * @cfg {String} tooltip (Optional)
59547      */
59548     /**
59549      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
59550      */
59551     /**
59552      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
59553      */
59554     /**
59555      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
59556      */
59557     /**
59558      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
59559      */
59560         /**
59561      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
59562      */
59563     /**
59564      * Returns the id of the column at the specified index.
59565      * @param {Number} index The column index
59566      * @return {String} the id
59567      */
59568     getColumnId : function(index){
59569         return this.config[index].id;
59570     },
59571
59572     /**
59573      * Returns the column for a specified id.
59574      * @param {String} id The column id
59575      * @return {Object} the column
59576      */
59577     getColumnById : function(id){
59578         return this.lookup[id];
59579     },
59580
59581     
59582     /**
59583      * Returns the column Object for a specified dataIndex.
59584      * @param {String} dataIndex The column dataIndex
59585      * @return {Object|Boolean} the column or false if not found
59586      */
59587     getColumnByDataIndex: function(dataIndex){
59588         var index = this.findColumnIndex(dataIndex);
59589         return index > -1 ? this.config[index] : false;
59590     },
59591     
59592     /**
59593      * Returns the index for a specified column id.
59594      * @param {String} id The column id
59595      * @return {Number} the index, or -1 if not found
59596      */
59597     getIndexById : function(id){
59598         for(var i = 0, len = this.config.length; i < len; i++){
59599             if(this.config[i].id == id){
59600                 return i;
59601             }
59602         }
59603         return -1;
59604     },
59605     
59606     /**
59607      * Returns the index for a specified column dataIndex.
59608      * @param {String} dataIndex The column dataIndex
59609      * @return {Number} the index, or -1 if not found
59610      */
59611     
59612     findColumnIndex : function(dataIndex){
59613         for(var i = 0, len = this.config.length; i < len; i++){
59614             if(this.config[i].dataIndex == dataIndex){
59615                 return i;
59616             }
59617         }
59618         return -1;
59619     },
59620     
59621     
59622     moveColumn : function(oldIndex, newIndex){
59623         var c = this.config[oldIndex];
59624         this.config.splice(oldIndex, 1);
59625         this.config.splice(newIndex, 0, c);
59626         this.dataMap = null;
59627         this.fireEvent("columnmoved", this, oldIndex, newIndex);
59628     },
59629
59630     isLocked : function(colIndex){
59631         return this.config[colIndex].locked === true;
59632     },
59633
59634     setLocked : function(colIndex, value, suppressEvent){
59635         if(this.isLocked(colIndex) == value){
59636             return;
59637         }
59638         this.config[colIndex].locked = value;
59639         if(!suppressEvent){
59640             this.fireEvent("columnlockchange", this, colIndex, value);
59641         }
59642     },
59643
59644     getTotalLockedWidth : function(){
59645         var totalWidth = 0;
59646         for(var i = 0; i < this.config.length; i++){
59647             if(this.isLocked(i) && !this.isHidden(i)){
59648                 this.totalWidth += this.getColumnWidth(i);
59649             }
59650         }
59651         return totalWidth;
59652     },
59653
59654     getLockedCount : function(){
59655         for(var i = 0, len = this.config.length; i < len; i++){
59656             if(!this.isLocked(i)){
59657                 return i;
59658             }
59659         }
59660         
59661         return this.config.length;
59662     },
59663
59664     /**
59665      * Returns the number of columns.
59666      * @return {Number}
59667      */
59668     getColumnCount : function(visibleOnly){
59669         if(visibleOnly === true){
59670             var c = 0;
59671             for(var i = 0, len = this.config.length; i < len; i++){
59672                 if(!this.isHidden(i)){
59673                     c++;
59674                 }
59675             }
59676             return c;
59677         }
59678         return this.config.length;
59679     },
59680
59681     /**
59682      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
59683      * @param {Function} fn
59684      * @param {Object} scope (optional)
59685      * @return {Array} result
59686      */
59687     getColumnsBy : function(fn, scope){
59688         var r = [];
59689         for(var i = 0, len = this.config.length; i < len; i++){
59690             var c = this.config[i];
59691             if(fn.call(scope||this, c, i) === true){
59692                 r[r.length] = c;
59693             }
59694         }
59695         return r;
59696     },
59697
59698     /**
59699      * Returns true if the specified column is sortable.
59700      * @param {Number} col The column index
59701      * @return {Boolean}
59702      */
59703     isSortable : function(col){
59704         if(typeof this.config[col].sortable == "undefined"){
59705             return this.defaultSortable;
59706         }
59707         return this.config[col].sortable;
59708     },
59709
59710     /**
59711      * Returns the rendering (formatting) function defined for the column.
59712      * @param {Number} col The column index.
59713      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
59714      */
59715     getRenderer : function(col){
59716         if(!this.config[col].renderer){
59717             return Roo.grid.ColumnModel.defaultRenderer;
59718         }
59719         return this.config[col].renderer;
59720     },
59721
59722     /**
59723      * Sets the rendering (formatting) function for a column.
59724      * @param {Number} col The column index
59725      * @param {Function} fn The function to use to process the cell's raw data
59726      * to return HTML markup for the grid view. The render function is called with
59727      * the following parameters:<ul>
59728      * <li>Data value.</li>
59729      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
59730      * <li>css A CSS style string to apply to the table cell.</li>
59731      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
59732      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
59733      * <li>Row index</li>
59734      * <li>Column index</li>
59735      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
59736      */
59737     setRenderer : function(col, fn){
59738         this.config[col].renderer = fn;
59739     },
59740
59741     /**
59742      * Returns the width for the specified column.
59743      * @param {Number} col The column index
59744      * @param (optional) {String} gridSize bootstrap width size.
59745      * @return {Number}
59746      */
59747     getColumnWidth : function(col, gridSize)
59748         {
59749                 var cfg = this.config[col];
59750                 
59751                 if (typeof(gridSize) == 'undefined') {
59752                         return cfg.width * 1 || this.defaultWidth;
59753                 }
59754                 if (gridSize === false) { // if we set it..
59755                         return cfg.width || false;
59756                 }
59757                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
59758                 
59759                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
59760                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
59761                                 continue;
59762                         }
59763                         return cfg[ sizes[i] ];
59764                 }
59765                 return 1;
59766                 
59767     },
59768
59769     /**
59770      * Sets the width for a column.
59771      * @param {Number} col The column index
59772      * @param {Number} width The new width
59773      */
59774     setColumnWidth : function(col, width, suppressEvent){
59775         this.config[col].width = width;
59776         this.totalWidth = null;
59777         if(!suppressEvent){
59778              this.fireEvent("widthchange", this, col, width);
59779         }
59780     },
59781
59782     /**
59783      * Returns the total width of all columns.
59784      * @param {Boolean} includeHidden True to include hidden column widths
59785      * @return {Number}
59786      */
59787     getTotalWidth : function(includeHidden){
59788         if(!this.totalWidth){
59789             this.totalWidth = 0;
59790             for(var i = 0, len = this.config.length; i < len; i++){
59791                 if(includeHidden || !this.isHidden(i)){
59792                     this.totalWidth += this.getColumnWidth(i);
59793                 }
59794             }
59795         }
59796         return this.totalWidth;
59797     },
59798
59799     /**
59800      * Returns the header for the specified column.
59801      * @param {Number} col The column index
59802      * @return {String}
59803      */
59804     getColumnHeader : function(col){
59805         return this.config[col].header;
59806     },
59807
59808     /**
59809      * Sets the header for a column.
59810      * @param {Number} col The column index
59811      * @param {String} header The new header
59812      */
59813     setColumnHeader : function(col, header){
59814         this.config[col].header = header;
59815         this.fireEvent("headerchange", this, col, header);
59816     },
59817
59818     /**
59819      * Returns the tooltip for the specified column.
59820      * @param {Number} col The column index
59821      * @return {String}
59822      */
59823     getColumnTooltip : function(col){
59824             return this.config[col].tooltip;
59825     },
59826     /**
59827      * Sets the tooltip for a column.
59828      * @param {Number} col The column index
59829      * @param {String} tooltip The new tooltip
59830      */
59831     setColumnTooltip : function(col, tooltip){
59832             this.config[col].tooltip = tooltip;
59833     },
59834
59835     /**
59836      * Returns the dataIndex for the specified column.
59837      * @param {Number} col The column index
59838      * @return {Number}
59839      */
59840     getDataIndex : function(col){
59841         return this.config[col].dataIndex;
59842     },
59843
59844     /**
59845      * Sets the dataIndex for a column.
59846      * @param {Number} col The column index
59847      * @param {Number} dataIndex The new dataIndex
59848      */
59849     setDataIndex : function(col, dataIndex){
59850         this.config[col].dataIndex = dataIndex;
59851     },
59852
59853     
59854     
59855     /**
59856      * Returns true if the cell is editable.
59857      * @param {Number} colIndex The column index
59858      * @param {Number} rowIndex The row index - this is nto actually used..?
59859      * @return {Boolean}
59860      */
59861     isCellEditable : function(colIndex, rowIndex){
59862         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
59863     },
59864
59865     /**
59866      * Returns the editor defined for the cell/column.
59867      * return false or null to disable editing.
59868      * @param {Number} colIndex The column index
59869      * @param {Number} rowIndex The row index
59870      * @return {Object}
59871      */
59872     getCellEditor : function(colIndex, rowIndex){
59873         return this.config[colIndex].editor;
59874     },
59875
59876     /**
59877      * Sets if a column is editable.
59878      * @param {Number} col The column index
59879      * @param {Boolean} editable True if the column is editable
59880      */
59881     setEditable : function(col, editable){
59882         this.config[col].editable = editable;
59883     },
59884
59885
59886     /**
59887      * Returns true if the column is hidden.
59888      * @param {Number} colIndex The column index
59889      * @return {Boolean}
59890      */
59891     isHidden : function(colIndex){
59892         return this.config[colIndex].hidden;
59893     },
59894
59895
59896     /**
59897      * Returns true if the column width cannot be changed
59898      */
59899     isFixed : function(colIndex){
59900         return this.config[colIndex].fixed;
59901     },
59902
59903     /**
59904      * Returns true if the column can be resized
59905      * @return {Boolean}
59906      */
59907     isResizable : function(colIndex){
59908         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
59909     },
59910     /**
59911      * Sets if a column is hidden.
59912      * @param {Number} colIndex The column index
59913      * @param {Boolean} hidden True if the column is hidden
59914      */
59915     setHidden : function(colIndex, hidden){
59916         this.config[colIndex].hidden = hidden;
59917         this.totalWidth = null;
59918         this.fireEvent("hiddenchange", this, colIndex, hidden);
59919     },
59920
59921     /**
59922      * Sets the editor for a column.
59923      * @param {Number} col The column index
59924      * @param {Object} editor The editor object
59925      */
59926     setEditor : function(col, editor){
59927         this.config[col].editor = editor;
59928     },
59929     /**
59930      * Add a column (experimental...) - defaults to adding to the end..
59931      * @param {Object} config 
59932     */
59933     addColumn : function(c)
59934     {
59935     
59936         var i = this.config.length;
59937         this.config[i] = c;
59938         
59939         if(typeof c.dataIndex == "undefined"){
59940             c.dataIndex = i;
59941         }
59942         if(typeof c.renderer == "string"){
59943             c.renderer = Roo.util.Format[c.renderer];
59944         }
59945         if(typeof c.id == "undefined"){
59946             c.id = Roo.id();
59947         }
59948         if(c.editor && c.editor.xtype){
59949             c.editor  = Roo.factory(c.editor, Roo.grid);
59950         }
59951         if(c.editor && c.editor.isFormField){
59952             c.editor = new Roo.grid.GridEditor(c.editor);
59953         }
59954         this.lookup[c.id] = c;
59955     }
59956     
59957 });
59958
59959 Roo.grid.ColumnModel.defaultRenderer = function(value)
59960 {
59961     if(typeof value == "object") {
59962         return value;
59963     }
59964         if(typeof value == "string" && value.length < 1){
59965             return "&#160;";
59966         }
59967     
59968         return String.format("{0}", value);
59969 };
59970
59971 // Alias for backwards compatibility
59972 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
59973 /*
59974  * Based on:
59975  * Ext JS Library 1.1.1
59976  * Copyright(c) 2006-2007, Ext JS, LLC.
59977  *
59978  * Originally Released Under LGPL - original licence link has changed is not relivant.
59979  *
59980  * Fork - LGPL
59981  * <script type="text/javascript">
59982  */
59983
59984 /**
59985  * @class Roo.grid.AbstractSelectionModel
59986  * @extends Roo.util.Observable
59987  * @abstract
59988  * Abstract base class for grid SelectionModels.  It provides the interface that should be
59989  * implemented by descendant classes.  This class should not be directly instantiated.
59990  * @constructor
59991  */
59992 Roo.grid.AbstractSelectionModel = function(){
59993     this.locked = false;
59994     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
59995 };
59996
59997 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
59998     /** @ignore Called by the grid automatically. Do not call directly. */
59999     init : function(grid){
60000         this.grid = grid;
60001         this.initEvents();
60002     },
60003
60004     /**
60005      * Locks the selections.
60006      */
60007     lock : function(){
60008         this.locked = true;
60009     },
60010
60011     /**
60012      * Unlocks the selections.
60013      */
60014     unlock : function(){
60015         this.locked = false;
60016     },
60017
60018     /**
60019      * Returns true if the selections are locked.
60020      * @return {Boolean}
60021      */
60022     isLocked : function(){
60023         return this.locked;
60024     }
60025 });/*
60026  * Based on:
60027  * Ext JS Library 1.1.1
60028  * Copyright(c) 2006-2007, Ext JS, LLC.
60029  *
60030  * Originally Released Under LGPL - original licence link has changed is not relivant.
60031  *
60032  * Fork - LGPL
60033  * <script type="text/javascript">
60034  */
60035 /**
60036  * @extends Roo.grid.AbstractSelectionModel
60037  * @class Roo.grid.RowSelectionModel
60038  * The default SelectionModel used by {@link Roo.grid.Grid}.
60039  * It supports multiple selections and keyboard selection/navigation. 
60040  * @constructor
60041  * @param {Object} config
60042  */
60043 Roo.grid.RowSelectionModel = function(config){
60044     Roo.apply(this, config);
60045     this.selections = new Roo.util.MixedCollection(false, function(o){
60046         return o.id;
60047     });
60048
60049     this.last = false;
60050     this.lastActive = false;
60051
60052     this.addEvents({
60053         /**
60054         * @event selectionchange
60055         * Fires when the selection changes
60056         * @param {SelectionModel} this
60057         */
60058        "selectionchange" : true,
60059        /**
60060         * @event afterselectionchange
60061         * Fires after the selection changes (eg. by key press or clicking)
60062         * @param {SelectionModel} this
60063         */
60064        "afterselectionchange" : true,
60065        /**
60066         * @event beforerowselect
60067         * Fires when a row is selected being selected, return false to cancel.
60068         * @param {SelectionModel} this
60069         * @param {Number} rowIndex The selected index
60070         * @param {Boolean} keepExisting False if other selections will be cleared
60071         */
60072        "beforerowselect" : true,
60073        /**
60074         * @event rowselect
60075         * Fires when a row is selected.
60076         * @param {SelectionModel} this
60077         * @param {Number} rowIndex The selected index
60078         * @param {Roo.data.Record} r The record
60079         */
60080        "rowselect" : true,
60081        /**
60082         * @event rowdeselect
60083         * Fires when a row is deselected.
60084         * @param {SelectionModel} this
60085         * @param {Number} rowIndex The selected index
60086         */
60087         "rowdeselect" : true
60088     });
60089     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
60090     this.locked = false;
60091 };
60092
60093 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
60094     /**
60095      * @cfg {Boolean} singleSelect
60096      * True to allow selection of only one row at a time (defaults to false)
60097      */
60098     singleSelect : false,
60099
60100     // private
60101     initEvents : function(){
60102
60103         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
60104             this.grid.on("mousedown", this.handleMouseDown, this);
60105         }else{ // allow click to work like normal
60106             this.grid.on("rowclick", this.handleDragableRowClick, this);
60107         }
60108         // bootstrap does not have a view..
60109         var view = this.grid.view ? this.grid.view : this.grid;
60110         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
60111             "up" : function(e){
60112                 if(!e.shiftKey){
60113                     this.selectPrevious(e.shiftKey);
60114                 }else if(this.last !== false && this.lastActive !== false){
60115                     var last = this.last;
60116                     this.selectRange(this.last,  this.lastActive-1);
60117                     view.focusRow(this.lastActive);
60118                     if(last !== false){
60119                         this.last = last;
60120                     }
60121                 }else{
60122                     this.selectFirstRow();
60123                 }
60124                 this.fireEvent("afterselectionchange", this);
60125             },
60126             "down" : function(e){
60127                 if(!e.shiftKey){
60128                     this.selectNext(e.shiftKey);
60129                 }else if(this.last !== false && this.lastActive !== false){
60130                     var last = this.last;
60131                     this.selectRange(this.last,  this.lastActive+1);
60132                     view.focusRow(this.lastActive);
60133                     if(last !== false){
60134                         this.last = last;
60135                     }
60136                 }else{
60137                     this.selectFirstRow();
60138                 }
60139                 this.fireEvent("afterselectionchange", this);
60140             },
60141             scope: this
60142         });
60143
60144          
60145         view.on("refresh", this.onRefresh, this);
60146         view.on("rowupdated", this.onRowUpdated, this);
60147         view.on("rowremoved", this.onRemove, this);
60148     },
60149
60150     // private
60151     onRefresh : function(){
60152         var ds = this.grid.ds, i, v = this.grid.view;
60153         var s = this.selections;
60154         s.each(function(r){
60155             if((i = ds.indexOfId(r.id)) != -1){
60156                 v.onRowSelect(i);
60157                 s.add(ds.getAt(i)); // updating the selection relate data
60158             }else{
60159                 s.remove(r);
60160             }
60161         });
60162     },
60163
60164     // private
60165     onRemove : function(v, index, r){
60166         this.selections.remove(r);
60167     },
60168
60169     // private
60170     onRowUpdated : function(v, index, r){
60171         if(this.isSelected(r)){
60172             v.onRowSelect(index);
60173         }
60174     },
60175
60176     /**
60177      * Select records.
60178      * @param {Array} records The records to select
60179      * @param {Boolean} keepExisting (optional) True to keep existing selections
60180      */
60181     selectRecords : function(records, keepExisting){
60182         if(!keepExisting){
60183             this.clearSelections();
60184         }
60185         var ds = this.grid.ds;
60186         for(var i = 0, len = records.length; i < len; i++){
60187             this.selectRow(ds.indexOf(records[i]), true);
60188         }
60189     },
60190
60191     /**
60192      * Gets the number of selected rows.
60193      * @return {Number}
60194      */
60195     getCount : function(){
60196         return this.selections.length;
60197     },
60198
60199     /**
60200      * Selects the first row in the grid.
60201      */
60202     selectFirstRow : function(){
60203         this.selectRow(0);
60204     },
60205
60206     /**
60207      * Select the last row.
60208      * @param {Boolean} keepExisting (optional) True to keep existing selections
60209      */
60210     selectLastRow : function(keepExisting){
60211         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
60212     },
60213
60214     /**
60215      * Selects the row immediately following the last selected row.
60216      * @param {Boolean} keepExisting (optional) True to keep existing selections
60217      */
60218     selectNext : function(keepExisting){
60219         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
60220             this.selectRow(this.last+1, keepExisting);
60221             var view = this.grid.view ? this.grid.view : this.grid;
60222             view.focusRow(this.last);
60223         }
60224     },
60225
60226     /**
60227      * Selects the row that precedes the last selected row.
60228      * @param {Boolean} keepExisting (optional) True to keep existing selections
60229      */
60230     selectPrevious : function(keepExisting){
60231         if(this.last){
60232             this.selectRow(this.last-1, keepExisting);
60233             var view = this.grid.view ? this.grid.view : this.grid;
60234             view.focusRow(this.last);
60235         }
60236     },
60237
60238     /**
60239      * Returns the selected records
60240      * @return {Array} Array of selected records
60241      */
60242     getSelections : function(){
60243         return [].concat(this.selections.items);
60244     },
60245
60246     /**
60247      * Returns the first selected record.
60248      * @return {Record}
60249      */
60250     getSelected : function(){
60251         return this.selections.itemAt(0);
60252     },
60253
60254
60255     /**
60256      * Clears all selections.
60257      */
60258     clearSelections : function(fast){
60259         if(this.locked) {
60260             return;
60261         }
60262         if(fast !== true){
60263             var ds = this.grid.ds;
60264             var s = this.selections;
60265             s.each(function(r){
60266                 this.deselectRow(ds.indexOfId(r.id));
60267             }, this);
60268             s.clear();
60269         }else{
60270             this.selections.clear();
60271         }
60272         this.last = false;
60273     },
60274
60275
60276     /**
60277      * Selects all rows.
60278      */
60279     selectAll : function(){
60280         if(this.locked) {
60281             return;
60282         }
60283         this.selections.clear();
60284         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60285             this.selectRow(i, true);
60286         }
60287     },
60288
60289     /**
60290      * Returns True if there is a selection.
60291      * @return {Boolean}
60292      */
60293     hasSelection : function(){
60294         return this.selections.length > 0;
60295     },
60296
60297     /**
60298      * Returns True if the specified row is selected.
60299      * @param {Number/Record} record The record or index of the record to check
60300      * @return {Boolean}
60301      */
60302     isSelected : function(index){
60303         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60304         return (r && this.selections.key(r.id) ? true : false);
60305     },
60306
60307     /**
60308      * Returns True if the specified record id is selected.
60309      * @param {String} id The id of record to check
60310      * @return {Boolean}
60311      */
60312     isIdSelected : function(id){
60313         return (this.selections.key(id) ? true : false);
60314     },
60315
60316     // private
60317     handleMouseDown : function(e, t)
60318     {
60319         var view = this.grid.view ? this.grid.view : this.grid;
60320         var rowIndex;
60321         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60322             return;
60323         };
60324         if(e.shiftKey && this.last !== false){
60325             var last = this.last;
60326             this.selectRange(last, rowIndex, e.ctrlKey);
60327             this.last = last; // reset the last
60328             view.focusRow(rowIndex);
60329         }else{
60330             var isSelected = this.isSelected(rowIndex);
60331             if(e.button !== 0 && isSelected){
60332                 view.focusRow(rowIndex);
60333             }else if(e.ctrlKey && isSelected){
60334                 this.deselectRow(rowIndex);
60335             }else if(!isSelected){
60336                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60337                 view.focusRow(rowIndex);
60338             }
60339         }
60340         this.fireEvent("afterselectionchange", this);
60341     },
60342     // private
60343     handleDragableRowClick :  function(grid, rowIndex, e) 
60344     {
60345         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
60346             this.selectRow(rowIndex, false);
60347             var view = this.grid.view ? this.grid.view : this.grid;
60348             view.focusRow(rowIndex);
60349              this.fireEvent("afterselectionchange", this);
60350         }
60351     },
60352     
60353     /**
60354      * Selects multiple rows.
60355      * @param {Array} rows Array of the indexes of the row to select
60356      * @param {Boolean} keepExisting (optional) True to keep existing selections
60357      */
60358     selectRows : function(rows, keepExisting){
60359         if(!keepExisting){
60360             this.clearSelections();
60361         }
60362         for(var i = 0, len = rows.length; i < len; i++){
60363             this.selectRow(rows[i], true);
60364         }
60365     },
60366
60367     /**
60368      * Selects a range of rows. All rows in between startRow and endRow are also selected.
60369      * @param {Number} startRow The index of the first row in the range
60370      * @param {Number} endRow The index of the last row in the range
60371      * @param {Boolean} keepExisting (optional) True to retain existing selections
60372      */
60373     selectRange : function(startRow, endRow, keepExisting){
60374         if(this.locked) {
60375             return;
60376         }
60377         if(!keepExisting){
60378             this.clearSelections();
60379         }
60380         if(startRow <= endRow){
60381             for(var i = startRow; i <= endRow; i++){
60382                 this.selectRow(i, true);
60383             }
60384         }else{
60385             for(var i = startRow; i >= endRow; i--){
60386                 this.selectRow(i, true);
60387             }
60388         }
60389     },
60390
60391     /**
60392      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
60393      * @param {Number} startRow The index of the first row in the range
60394      * @param {Number} endRow The index of the last row in the range
60395      */
60396     deselectRange : function(startRow, endRow, preventViewNotify){
60397         if(this.locked) {
60398             return;
60399         }
60400         for(var i = startRow; i <= endRow; i++){
60401             this.deselectRow(i, preventViewNotify);
60402         }
60403     },
60404
60405     /**
60406      * Selects a row.
60407      * @param {Number} row The index of the row to select
60408      * @param {Boolean} keepExisting (optional) True to keep existing selections
60409      */
60410     selectRow : function(index, keepExisting, preventViewNotify){
60411         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
60412             return;
60413         }
60414         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
60415             if(!keepExisting || this.singleSelect){
60416                 this.clearSelections();
60417             }
60418             var r = this.grid.ds.getAt(index);
60419             this.selections.add(r);
60420             this.last = this.lastActive = index;
60421             if(!preventViewNotify){
60422                 var view = this.grid.view ? this.grid.view : this.grid;
60423                 view.onRowSelect(index);
60424             }
60425             this.fireEvent("rowselect", this, index, r);
60426             this.fireEvent("selectionchange", this);
60427         }
60428     },
60429
60430     /**
60431      * Deselects a row.
60432      * @param {Number} row The index of the row to deselect
60433      */
60434     deselectRow : function(index, preventViewNotify){
60435         if(this.locked) {
60436             return;
60437         }
60438         if(this.last == index){
60439             this.last = false;
60440         }
60441         if(this.lastActive == index){
60442             this.lastActive = false;
60443         }
60444         var r = this.grid.ds.getAt(index);
60445         this.selections.remove(r);
60446         if(!preventViewNotify){
60447             var view = this.grid.view ? this.grid.view : this.grid;
60448             view.onRowDeselect(index);
60449         }
60450         this.fireEvent("rowdeselect", this, index);
60451         this.fireEvent("selectionchange", this);
60452     },
60453
60454     // private
60455     restoreLast : function(){
60456         if(this._last){
60457             this.last = this._last;
60458         }
60459     },
60460
60461     // private
60462     acceptsNav : function(row, col, cm){
60463         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60464     },
60465
60466     // private
60467     onEditorKey : function(field, e){
60468         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
60469         if(k == e.TAB){
60470             e.stopEvent();
60471             ed.completeEdit();
60472             if(e.shiftKey){
60473                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60474             }else{
60475                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60476             }
60477         }else if(k == e.ENTER && !e.ctrlKey){
60478             e.stopEvent();
60479             ed.completeEdit();
60480             if(e.shiftKey){
60481                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
60482             }else{
60483                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
60484             }
60485         }else if(k == e.ESC){
60486             ed.cancelEdit();
60487         }
60488         if(newCell){
60489             g.startEditing(newCell[0], newCell[1]);
60490         }
60491     }
60492 });/*
60493  * Based on:
60494  * Ext JS Library 1.1.1
60495  * Copyright(c) 2006-2007, Ext JS, LLC.
60496  *
60497  * Originally Released Under LGPL - original licence link has changed is not relivant.
60498  *
60499  * Fork - LGPL
60500  * <script type="text/javascript">
60501  */
60502 /**
60503  * @class Roo.grid.CellSelectionModel
60504  * @extends Roo.grid.AbstractSelectionModel
60505  * This class provides the basic implementation for cell selection in a grid.
60506  * @constructor
60507  * @param {Object} config The object containing the configuration of this model.
60508  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
60509  */
60510 Roo.grid.CellSelectionModel = function(config){
60511     Roo.apply(this, config);
60512
60513     this.selection = null;
60514
60515     this.addEvents({
60516         /**
60517              * @event beforerowselect
60518              * Fires before a cell is selected.
60519              * @param {SelectionModel} this
60520              * @param {Number} rowIndex The selected row index
60521              * @param {Number} colIndex The selected cell index
60522              */
60523             "beforecellselect" : true,
60524         /**
60525              * @event cellselect
60526              * Fires when a cell is selected.
60527              * @param {SelectionModel} this
60528              * @param {Number} rowIndex The selected row index
60529              * @param {Number} colIndex The selected cell index
60530              */
60531             "cellselect" : true,
60532         /**
60533              * @event selectionchange
60534              * Fires when the active selection changes.
60535              * @param {SelectionModel} this
60536              * @param {Object} selection null for no selection or an object (o) with two properties
60537                 <ul>
60538                 <li>o.record: the record object for the row the selection is in</li>
60539                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
60540                 </ul>
60541              */
60542             "selectionchange" : true,
60543         /**
60544              * @event tabend
60545              * Fires when the tab (or enter) was pressed on the last editable cell
60546              * You can use this to trigger add new row.
60547              * @param {SelectionModel} this
60548              */
60549             "tabend" : true,
60550          /**
60551              * @event beforeeditnext
60552              * Fires before the next editable sell is made active
60553              * You can use this to skip to another cell or fire the tabend
60554              *    if you set cell to false
60555              * @param {Object} eventdata object : { cell : [ row, col ] } 
60556              */
60557             "beforeeditnext" : true
60558     });
60559     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
60560 };
60561
60562 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
60563     
60564     enter_is_tab: false,
60565
60566     /** @ignore */
60567     initEvents : function(){
60568         this.grid.on("mousedown", this.handleMouseDown, this);
60569         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
60570         var view = this.grid.view;
60571         view.on("refresh", this.onViewChange, this);
60572         view.on("rowupdated", this.onRowUpdated, this);
60573         view.on("beforerowremoved", this.clearSelections, this);
60574         view.on("beforerowsinserted", this.clearSelections, this);
60575         if(this.grid.isEditor){
60576             this.grid.on("beforeedit", this.beforeEdit,  this);
60577         }
60578     },
60579
60580         //private
60581     beforeEdit : function(e){
60582         this.select(e.row, e.column, false, true, e.record);
60583     },
60584
60585         //private
60586     onRowUpdated : function(v, index, r){
60587         if(this.selection && this.selection.record == r){
60588             v.onCellSelect(index, this.selection.cell[1]);
60589         }
60590     },
60591
60592         //private
60593     onViewChange : function(){
60594         this.clearSelections(true);
60595     },
60596
60597         /**
60598          * Returns the currently selected cell,.
60599          * @return {Array} The selected cell (row, column) or null if none selected.
60600          */
60601     getSelectedCell : function(){
60602         return this.selection ? this.selection.cell : null;
60603     },
60604
60605     /**
60606      * Clears all selections.
60607      * @param {Boolean} true to prevent the gridview from being notified about the change.
60608      */
60609     clearSelections : function(preventNotify){
60610         var s = this.selection;
60611         if(s){
60612             if(preventNotify !== true){
60613                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
60614             }
60615             this.selection = null;
60616             this.fireEvent("selectionchange", this, null);
60617         }
60618     },
60619
60620     /**
60621      * Returns true if there is a selection.
60622      * @return {Boolean}
60623      */
60624     hasSelection : function(){
60625         return this.selection ? true : false;
60626     },
60627
60628     /** @ignore */
60629     handleMouseDown : function(e, t){
60630         var v = this.grid.getView();
60631         if(this.isLocked()){
60632             return;
60633         };
60634         var row = v.findRowIndex(t);
60635         var cell = v.findCellIndex(t);
60636         if(row !== false && cell !== false){
60637             this.select(row, cell);
60638         }
60639     },
60640
60641     /**
60642      * Selects a cell.
60643      * @param {Number} rowIndex
60644      * @param {Number} collIndex
60645      */
60646     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
60647         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
60648             this.clearSelections();
60649             r = r || this.grid.dataSource.getAt(rowIndex);
60650             this.selection = {
60651                 record : r,
60652                 cell : [rowIndex, colIndex]
60653             };
60654             if(!preventViewNotify){
60655                 var v = this.grid.getView();
60656                 v.onCellSelect(rowIndex, colIndex);
60657                 if(preventFocus !== true){
60658                     v.focusCell(rowIndex, colIndex);
60659                 }
60660             }
60661             this.fireEvent("cellselect", this, rowIndex, colIndex);
60662             this.fireEvent("selectionchange", this, this.selection);
60663         }
60664     },
60665
60666         //private
60667     isSelectable : function(rowIndex, colIndex, cm){
60668         return !cm.isHidden(colIndex);
60669     },
60670
60671     /** @ignore */
60672     handleKeyDown : function(e){
60673         //Roo.log('Cell Sel Model handleKeyDown');
60674         if(!e.isNavKeyPress()){
60675             return;
60676         }
60677         var g = this.grid, s = this.selection;
60678         if(!s){
60679             e.stopEvent();
60680             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
60681             if(cell){
60682                 this.select(cell[0], cell[1]);
60683             }
60684             return;
60685         }
60686         var sm = this;
60687         var walk = function(row, col, step){
60688             return g.walkCells(row, col, step, sm.isSelectable,  sm);
60689         };
60690         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
60691         var newCell;
60692
60693       
60694
60695         switch(k){
60696             case e.TAB:
60697                 // handled by onEditorKey
60698                 if (g.isEditor && g.editing) {
60699                     return;
60700                 }
60701                 if(e.shiftKey) {
60702                     newCell = walk(r, c-1, -1);
60703                 } else {
60704                     newCell = walk(r, c+1, 1);
60705                 }
60706                 break;
60707             
60708             case e.DOWN:
60709                newCell = walk(r+1, c, 1);
60710                 break;
60711             
60712             case e.UP:
60713                 newCell = walk(r-1, c, -1);
60714                 break;
60715             
60716             case e.RIGHT:
60717                 newCell = walk(r, c+1, 1);
60718                 break;
60719             
60720             case e.LEFT:
60721                 newCell = walk(r, c-1, -1);
60722                 break;
60723             
60724             case e.ENTER:
60725                 
60726                 if(g.isEditor && !g.editing){
60727                    g.startEditing(r, c);
60728                    e.stopEvent();
60729                    return;
60730                 }
60731                 
60732                 
60733              break;
60734         };
60735         if(newCell){
60736             this.select(newCell[0], newCell[1]);
60737             e.stopEvent();
60738             
60739         }
60740     },
60741
60742     acceptsNav : function(row, col, cm){
60743         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60744     },
60745     /**
60746      * Selects a cell.
60747      * @param {Number} field (not used) - as it's normally used as a listener
60748      * @param {Number} e - event - fake it by using
60749      *
60750      * var e = Roo.EventObjectImpl.prototype;
60751      * e.keyCode = e.TAB
60752      *
60753      * 
60754      */
60755     onEditorKey : function(field, e){
60756         
60757         var k = e.getKey(),
60758             newCell,
60759             g = this.grid,
60760             ed = g.activeEditor,
60761             forward = false;
60762         ///Roo.log('onEditorKey' + k);
60763         
60764         
60765         if (this.enter_is_tab && k == e.ENTER) {
60766             k = e.TAB;
60767         }
60768         
60769         if(k == e.TAB){
60770             if(e.shiftKey){
60771                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60772             }else{
60773                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60774                 forward = true;
60775             }
60776             
60777             e.stopEvent();
60778             
60779         } else if(k == e.ENTER &&  !e.ctrlKey){
60780             ed.completeEdit();
60781             e.stopEvent();
60782             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60783         
60784                 } else if(k == e.ESC){
60785             ed.cancelEdit();
60786         }
60787                 
60788         if (newCell) {
60789             var ecall = { cell : newCell, forward : forward };
60790             this.fireEvent('beforeeditnext', ecall );
60791             newCell = ecall.cell;
60792                         forward = ecall.forward;
60793         }
60794                 
60795         if(newCell){
60796             //Roo.log('next cell after edit');
60797             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
60798         } else if (forward) {
60799             // tabbed past last
60800             this.fireEvent.defer(100, this, ['tabend',this]);
60801         }
60802     }
60803 });/*
60804  * Based on:
60805  * Ext JS Library 1.1.1
60806  * Copyright(c) 2006-2007, Ext JS, LLC.
60807  *
60808  * Originally Released Under LGPL - original licence link has changed is not relivant.
60809  *
60810  * Fork - LGPL
60811  * <script type="text/javascript">
60812  */
60813  
60814 /**
60815  * @class Roo.grid.EditorGrid
60816  * @extends Roo.grid.Grid
60817  * Class for creating and editable grid.
60818  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
60819  * The container MUST have some type of size defined for the grid to fill. The container will be 
60820  * automatically set to position relative if it isn't already.
60821  * @param {Object} dataSource The data model to bind to
60822  * @param {Object} colModel The column model with info about this grid's columns
60823  */
60824 Roo.grid.EditorGrid = function(container, config){
60825     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
60826     this.getGridEl().addClass("xedit-grid");
60827
60828     if(!this.selModel){
60829         this.selModel = new Roo.grid.CellSelectionModel();
60830     }
60831
60832     this.activeEditor = null;
60833
60834         this.addEvents({
60835             /**
60836              * @event beforeedit
60837              * Fires before cell editing is triggered. The edit event object has the following properties <br />
60838              * <ul style="padding:5px;padding-left:16px;">
60839              * <li>grid - This grid</li>
60840              * <li>record - The record being edited</li>
60841              * <li>field - The field name being edited</li>
60842              * <li>value - The value for the field being edited.</li>
60843              * <li>row - The grid row index</li>
60844              * <li>column - The grid column index</li>
60845              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60846              * </ul>
60847              * @param {Object} e An edit event (see above for description)
60848              */
60849             "beforeedit" : true,
60850             /**
60851              * @event afteredit
60852              * Fires after a cell is edited. <br />
60853              * <ul style="padding:5px;padding-left:16px;">
60854              * <li>grid - This grid</li>
60855              * <li>record - The record being edited</li>
60856              * <li>field - The field name being edited</li>
60857              * <li>value - The value being set</li>
60858              * <li>originalValue - The original value for the field, before the edit.</li>
60859              * <li>row - The grid row index</li>
60860              * <li>column - The grid column index</li>
60861              * </ul>
60862              * @param {Object} e An edit event (see above for description)
60863              */
60864             "afteredit" : true,
60865             /**
60866              * @event validateedit
60867              * Fires after a cell is edited, but before the value is set in the record. 
60868          * You can use this to modify the value being set in the field, Return false
60869              * to cancel the change. The edit event object has the following properties <br />
60870              * <ul style="padding:5px;padding-left:16px;">
60871          * <li>editor - This editor</li>
60872              * <li>grid - This grid</li>
60873              * <li>record - The record being edited</li>
60874              * <li>field - The field name being edited</li>
60875              * <li>value - The value being set</li>
60876              * <li>originalValue - The original value for the field, before the edit.</li>
60877              * <li>row - The grid row index</li>
60878              * <li>column - The grid column index</li>
60879              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60880              * </ul>
60881              * @param {Object} e An edit event (see above for description)
60882              */
60883             "validateedit" : true
60884         });
60885     this.on("bodyscroll", this.stopEditing,  this);
60886     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
60887 };
60888
60889 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
60890     /**
60891      * @cfg {Number} clicksToEdit
60892      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
60893      */
60894     clicksToEdit: 2,
60895
60896     // private
60897     isEditor : true,
60898     // private
60899     trackMouseOver: false, // causes very odd FF errors
60900
60901     onCellDblClick : function(g, row, col){
60902         this.startEditing(row, col);
60903     },
60904
60905     onEditComplete : function(ed, value, startValue){
60906         this.editing = false;
60907         this.activeEditor = null;
60908         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
60909         var r = ed.record;
60910         var field = this.colModel.getDataIndex(ed.col);
60911         var e = {
60912             grid: this,
60913             record: r,
60914             field: field,
60915             originalValue: startValue,
60916             value: value,
60917             row: ed.row,
60918             column: ed.col,
60919             cancel:false,
60920             editor: ed
60921         };
60922         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
60923         cell.show();
60924           
60925         if(String(value) !== String(startValue)){
60926             
60927             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
60928                 r.set(field, e.value);
60929                 // if we are dealing with a combo box..
60930                 // then we also set the 'name' colum to be the displayField
60931                 if (ed.field.displayField && ed.field.name) {
60932                     r.set(ed.field.name, ed.field.el.dom.value);
60933                 }
60934                 
60935                 delete e.cancel; //?? why!!!
60936                 this.fireEvent("afteredit", e);
60937             }
60938         } else {
60939             this.fireEvent("afteredit", e); // always fire it!
60940         }
60941         this.view.focusCell(ed.row, ed.col);
60942     },
60943
60944     /**
60945      * Starts editing the specified for the specified row/column
60946      * @param {Number} rowIndex
60947      * @param {Number} colIndex
60948      */
60949     startEditing : function(row, col){
60950         this.stopEditing();
60951         if(this.colModel.isCellEditable(col, row)){
60952             this.view.ensureVisible(row, col, true);
60953           
60954             var r = this.dataSource.getAt(row);
60955             var field = this.colModel.getDataIndex(col);
60956             var cell = Roo.get(this.view.getCell(row,col));
60957             var e = {
60958                 grid: this,
60959                 record: r,
60960                 field: field,
60961                 value: r.data[field],
60962                 row: row,
60963                 column: col,
60964                 cancel:false 
60965             };
60966             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
60967                 this.editing = true;
60968                 var ed = this.colModel.getCellEditor(col, row);
60969                 
60970                 if (!ed) {
60971                     return;
60972                 }
60973                 if(!ed.rendered){
60974                     ed.render(ed.parentEl || document.body);
60975                 }
60976                 ed.field.reset();
60977                
60978                 cell.hide();
60979                 
60980                 (function(){ // complex but required for focus issues in safari, ie and opera
60981                     ed.row = row;
60982                     ed.col = col;
60983                     ed.record = r;
60984                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
60985                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
60986                     this.activeEditor = ed;
60987                     var v = r.data[field];
60988                     ed.startEdit(this.view.getCell(row, col), v);
60989                     // combo's with 'displayField and name set
60990                     if (ed.field.displayField && ed.field.name) {
60991                         ed.field.el.dom.value = r.data[ed.field.name];
60992                     }
60993                     
60994                     
60995                 }).defer(50, this);
60996             }
60997         }
60998     },
60999         
61000     /**
61001      * Stops any active editing
61002      */
61003     stopEditing : function(){
61004         if(this.activeEditor){
61005             this.activeEditor.completeEdit();
61006         }
61007         this.activeEditor = null;
61008     },
61009         
61010          /**
61011      * Called to get grid's drag proxy text, by default returns this.ddText.
61012      * @return {String}
61013      */
61014     getDragDropText : function(){
61015         var count = this.selModel.getSelectedCell() ? 1 : 0;
61016         return String.format(this.ddText, count, count == 1 ? '' : 's');
61017     }
61018         
61019 });/*
61020  * Based on:
61021  * Ext JS Library 1.1.1
61022  * Copyright(c) 2006-2007, Ext JS, LLC.
61023  *
61024  * Originally Released Under LGPL - original licence link has changed is not relivant.
61025  *
61026  * Fork - LGPL
61027  * <script type="text/javascript">
61028  */
61029
61030 // private - not really -- you end up using it !
61031 // This is a support class used internally by the Grid components
61032
61033 /**
61034  * @class Roo.grid.GridEditor
61035  * @extends Roo.Editor
61036  * Class for creating and editable grid elements.
61037  * @param {Object} config any settings (must include field)
61038  */
61039 Roo.grid.GridEditor = function(field, config){
61040     if (!config && field.field) {
61041         config = field;
61042         field = Roo.factory(config.field, Roo.form);
61043     }
61044     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
61045     field.monitorTab = false;
61046 };
61047
61048 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
61049     
61050     /**
61051      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
61052      */
61053     
61054     alignment: "tl-tl",
61055     autoSize: "width",
61056     hideEl : false,
61057     cls: "x-small-editor x-grid-editor",
61058     shim:false,
61059     shadow:"frame"
61060 });/*
61061  * Based on:
61062  * Ext JS Library 1.1.1
61063  * Copyright(c) 2006-2007, Ext JS, LLC.
61064  *
61065  * Originally Released Under LGPL - original licence link has changed is not relivant.
61066  *
61067  * Fork - LGPL
61068  * <script type="text/javascript">
61069  */
61070   
61071
61072   
61073 Roo.grid.PropertyRecord = Roo.data.Record.create([
61074     {name:'name',type:'string'},  'value'
61075 ]);
61076
61077
61078 Roo.grid.PropertyStore = function(grid, source){
61079     this.grid = grid;
61080     this.store = new Roo.data.Store({
61081         recordType : Roo.grid.PropertyRecord
61082     });
61083     this.store.on('update', this.onUpdate,  this);
61084     if(source){
61085         this.setSource(source);
61086     }
61087     Roo.grid.PropertyStore.superclass.constructor.call(this);
61088 };
61089
61090
61091
61092 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
61093     setSource : function(o){
61094         this.source = o;
61095         this.store.removeAll();
61096         var data = [];
61097         for(var k in o){
61098             if(this.isEditableValue(o[k])){
61099                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
61100             }
61101         }
61102         this.store.loadRecords({records: data}, {}, true);
61103     },
61104
61105     onUpdate : function(ds, record, type){
61106         if(type == Roo.data.Record.EDIT){
61107             var v = record.data['value'];
61108             var oldValue = record.modified['value'];
61109             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
61110                 this.source[record.id] = v;
61111                 record.commit();
61112                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
61113             }else{
61114                 record.reject();
61115             }
61116         }
61117     },
61118
61119     getProperty : function(row){
61120        return this.store.getAt(row);
61121     },
61122
61123     isEditableValue: function(val){
61124         if(val && val instanceof Date){
61125             return true;
61126         }else if(typeof val == 'object' || typeof val == 'function'){
61127             return false;
61128         }
61129         return true;
61130     },
61131
61132     setValue : function(prop, value){
61133         this.source[prop] = value;
61134         this.store.getById(prop).set('value', value);
61135     },
61136
61137     getSource : function(){
61138         return this.source;
61139     }
61140 });
61141
61142 Roo.grid.PropertyColumnModel = function(grid, store){
61143     this.grid = grid;
61144     var g = Roo.grid;
61145     g.PropertyColumnModel.superclass.constructor.call(this, [
61146         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
61147         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
61148     ]);
61149     this.store = store;
61150     this.bselect = Roo.DomHelper.append(document.body, {
61151         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
61152             {tag: 'option', value: 'true', html: 'true'},
61153             {tag: 'option', value: 'false', html: 'false'}
61154         ]
61155     });
61156     Roo.id(this.bselect);
61157     var f = Roo.form;
61158     this.editors = {
61159         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
61160         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
61161         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
61162         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
61163         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
61164     };
61165     this.renderCellDelegate = this.renderCell.createDelegate(this);
61166     this.renderPropDelegate = this.renderProp.createDelegate(this);
61167 };
61168
61169 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
61170     
61171     
61172     nameText : 'Name',
61173     valueText : 'Value',
61174     
61175     dateFormat : 'm/j/Y',
61176     
61177     
61178     renderDate : function(dateVal){
61179         return dateVal.dateFormat(this.dateFormat);
61180     },
61181
61182     renderBool : function(bVal){
61183         return bVal ? 'true' : 'false';
61184     },
61185
61186     isCellEditable : function(colIndex, rowIndex){
61187         return colIndex == 1;
61188     },
61189
61190     getRenderer : function(col){
61191         return col == 1 ?
61192             this.renderCellDelegate : this.renderPropDelegate;
61193     },
61194
61195     renderProp : function(v){
61196         return this.getPropertyName(v);
61197     },
61198
61199     renderCell : function(val){
61200         var rv = val;
61201         if(val instanceof Date){
61202             rv = this.renderDate(val);
61203         }else if(typeof val == 'boolean'){
61204             rv = this.renderBool(val);
61205         }
61206         return Roo.util.Format.htmlEncode(rv);
61207     },
61208
61209     getPropertyName : function(name){
61210         var pn = this.grid.propertyNames;
61211         return pn && pn[name] ? pn[name] : name;
61212     },
61213
61214     getCellEditor : function(colIndex, rowIndex){
61215         var p = this.store.getProperty(rowIndex);
61216         var n = p.data['name'], val = p.data['value'];
61217         
61218         if(typeof(this.grid.customEditors[n]) == 'string'){
61219             return this.editors[this.grid.customEditors[n]];
61220         }
61221         if(typeof(this.grid.customEditors[n]) != 'undefined'){
61222             return this.grid.customEditors[n];
61223         }
61224         if(val instanceof Date){
61225             return this.editors['date'];
61226         }else if(typeof val == 'number'){
61227             return this.editors['number'];
61228         }else if(typeof val == 'boolean'){
61229             return this.editors['boolean'];
61230         }else{
61231             return this.editors['string'];
61232         }
61233     }
61234 });
61235
61236 /**
61237  * @class Roo.grid.PropertyGrid
61238  * @extends Roo.grid.EditorGrid
61239  * This class represents the  interface of a component based property grid control.
61240  * <br><br>Usage:<pre><code>
61241  var grid = new Roo.grid.PropertyGrid("my-container-id", {
61242       
61243  });
61244  // set any options
61245  grid.render();
61246  * </code></pre>
61247   
61248  * @constructor
61249  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61250  * The container MUST have some type of size defined for the grid to fill. The container will be
61251  * automatically set to position relative if it isn't already.
61252  * @param {Object} config A config object that sets properties on this grid.
61253  */
61254 Roo.grid.PropertyGrid = function(container, config){
61255     config = config || {};
61256     var store = new Roo.grid.PropertyStore(this);
61257     this.store = store;
61258     var cm = new Roo.grid.PropertyColumnModel(this, store);
61259     store.store.sort('name', 'ASC');
61260     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
61261         ds: store.store,
61262         cm: cm,
61263         enableColLock:false,
61264         enableColumnMove:false,
61265         stripeRows:false,
61266         trackMouseOver: false,
61267         clicksToEdit:1
61268     }, config));
61269     this.getGridEl().addClass('x-props-grid');
61270     this.lastEditRow = null;
61271     this.on('columnresize', this.onColumnResize, this);
61272     this.addEvents({
61273          /**
61274              * @event beforepropertychange
61275              * Fires before a property changes (return false to stop?)
61276              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61277              * @param {String} id Record Id
61278              * @param {String} newval New Value
61279          * @param {String} oldval Old Value
61280              */
61281         "beforepropertychange": true,
61282         /**
61283              * @event propertychange
61284              * Fires after a property changes
61285              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61286              * @param {String} id Record Id
61287              * @param {String} newval New Value
61288          * @param {String} oldval Old Value
61289              */
61290         "propertychange": true
61291     });
61292     this.customEditors = this.customEditors || {};
61293 };
61294 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61295     
61296      /**
61297      * @cfg {Object} customEditors map of colnames=> custom editors.
61298      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61299      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61300      * false disables editing of the field.
61301          */
61302     
61303       /**
61304      * @cfg {Object} propertyNames map of property Names to their displayed value
61305          */
61306     
61307     render : function(){
61308         Roo.grid.PropertyGrid.superclass.render.call(this);
61309         this.autoSize.defer(100, this);
61310     },
61311
61312     autoSize : function(){
61313         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61314         if(this.view){
61315             this.view.fitColumns();
61316         }
61317     },
61318
61319     onColumnResize : function(){
61320         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61321         this.autoSize();
61322     },
61323     /**
61324      * Sets the data for the Grid
61325      * accepts a Key => Value object of all the elements avaiable.
61326      * @param {Object} data  to appear in grid.
61327      */
61328     setSource : function(source){
61329         this.store.setSource(source);
61330         //this.autoSize();
61331     },
61332     /**
61333      * Gets all the data from the grid.
61334      * @return {Object} data  data stored in grid
61335      */
61336     getSource : function(){
61337         return this.store.getSource();
61338     }
61339 });/*
61340   
61341  * Licence LGPL
61342  
61343  */
61344  
61345 /**
61346  * @class Roo.grid.Calendar
61347  * @extends Roo.grid.Grid
61348  * This class extends the Grid to provide a calendar widget
61349  * <br><br>Usage:<pre><code>
61350  var grid = new Roo.grid.Calendar("my-container-id", {
61351      ds: myDataStore,
61352      cm: myColModel,
61353      selModel: mySelectionModel,
61354      autoSizeColumns: true,
61355      monitorWindowResize: false,
61356      trackMouseOver: true
61357      eventstore : real data store..
61358  });
61359  // set any options
61360  grid.render();
61361   
61362   * @constructor
61363  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61364  * The container MUST have some type of size defined for the grid to fill. The container will be
61365  * automatically set to position relative if it isn't already.
61366  * @param {Object} config A config object that sets properties on this grid.
61367  */
61368 Roo.grid.Calendar = function(container, config){
61369         // initialize the container
61370         this.container = Roo.get(container);
61371         this.container.update("");
61372         this.container.setStyle("overflow", "hidden");
61373     this.container.addClass('x-grid-container');
61374
61375     this.id = this.container.id;
61376
61377     Roo.apply(this, config);
61378     // check and correct shorthanded configs
61379     
61380     var rows = [];
61381     var d =1;
61382     for (var r = 0;r < 6;r++) {
61383         
61384         rows[r]=[];
61385         for (var c =0;c < 7;c++) {
61386             rows[r][c]= '';
61387         }
61388     }
61389     if (this.eventStore) {
61390         this.eventStore= Roo.factory(this.eventStore, Roo.data);
61391         this.eventStore.on('load',this.onLoad, this);
61392         this.eventStore.on('beforeload',this.clearEvents, this);
61393          
61394     }
61395     
61396     this.dataSource = new Roo.data.Store({
61397             proxy: new Roo.data.MemoryProxy(rows),
61398             reader: new Roo.data.ArrayReader({}, [
61399                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
61400     });
61401
61402     this.dataSource.load();
61403     this.ds = this.dataSource;
61404     this.ds.xmodule = this.xmodule || false;
61405     
61406     
61407     var cellRender = function(v,x,r)
61408     {
61409         return String.format(
61410             '<div class="fc-day  fc-widget-content"><div>' +
61411                 '<div class="fc-event-container"></div>' +
61412                 '<div class="fc-day-number">{0}</div>'+
61413                 
61414                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
61415             '</div></div>', v);
61416     
61417     }
61418     
61419     
61420     this.colModel = new Roo.grid.ColumnModel( [
61421         {
61422             xtype: 'ColumnModel',
61423             xns: Roo.grid,
61424             dataIndex : 'weekday0',
61425             header : 'Sunday',
61426             renderer : cellRender
61427         },
61428         {
61429             xtype: 'ColumnModel',
61430             xns: Roo.grid,
61431             dataIndex : 'weekday1',
61432             header : 'Monday',
61433             renderer : cellRender
61434         },
61435         {
61436             xtype: 'ColumnModel',
61437             xns: Roo.grid,
61438             dataIndex : 'weekday2',
61439             header : 'Tuesday',
61440             renderer : cellRender
61441         },
61442         {
61443             xtype: 'ColumnModel',
61444             xns: Roo.grid,
61445             dataIndex : 'weekday3',
61446             header : 'Wednesday',
61447             renderer : cellRender
61448         },
61449         {
61450             xtype: 'ColumnModel',
61451             xns: Roo.grid,
61452             dataIndex : 'weekday4',
61453             header : 'Thursday',
61454             renderer : cellRender
61455         },
61456         {
61457             xtype: 'ColumnModel',
61458             xns: Roo.grid,
61459             dataIndex : 'weekday5',
61460             header : 'Friday',
61461             renderer : cellRender
61462         },
61463         {
61464             xtype: 'ColumnModel',
61465             xns: Roo.grid,
61466             dataIndex : 'weekday6',
61467             header : 'Saturday',
61468             renderer : cellRender
61469         }
61470     ]);
61471     this.cm = this.colModel;
61472     this.cm.xmodule = this.xmodule || false;
61473  
61474         
61475           
61476     //this.selModel = new Roo.grid.CellSelectionModel();
61477     //this.sm = this.selModel;
61478     //this.selModel.init(this);
61479     
61480     
61481     if(this.width){
61482         this.container.setWidth(this.width);
61483     }
61484
61485     if(this.height){
61486         this.container.setHeight(this.height);
61487     }
61488     /** @private */
61489         this.addEvents({
61490         // raw events
61491         /**
61492          * @event click
61493          * The raw click event for the entire grid.
61494          * @param {Roo.EventObject} e
61495          */
61496         "click" : true,
61497         /**
61498          * @event dblclick
61499          * The raw dblclick event for the entire grid.
61500          * @param {Roo.EventObject} e
61501          */
61502         "dblclick" : true,
61503         /**
61504          * @event contextmenu
61505          * The raw contextmenu event for the entire grid.
61506          * @param {Roo.EventObject} e
61507          */
61508         "contextmenu" : true,
61509         /**
61510          * @event mousedown
61511          * The raw mousedown event for the entire grid.
61512          * @param {Roo.EventObject} e
61513          */
61514         "mousedown" : true,
61515         /**
61516          * @event mouseup
61517          * The raw mouseup event for the entire grid.
61518          * @param {Roo.EventObject} e
61519          */
61520         "mouseup" : true,
61521         /**
61522          * @event mouseover
61523          * The raw mouseover event for the entire grid.
61524          * @param {Roo.EventObject} e
61525          */
61526         "mouseover" : true,
61527         /**
61528          * @event mouseout
61529          * The raw mouseout event for the entire grid.
61530          * @param {Roo.EventObject} e
61531          */
61532         "mouseout" : true,
61533         /**
61534          * @event keypress
61535          * The raw keypress event for the entire grid.
61536          * @param {Roo.EventObject} e
61537          */
61538         "keypress" : true,
61539         /**
61540          * @event keydown
61541          * The raw keydown event for the entire grid.
61542          * @param {Roo.EventObject} e
61543          */
61544         "keydown" : true,
61545
61546         // custom events
61547
61548         /**
61549          * @event cellclick
61550          * Fires when a cell is clicked
61551          * @param {Grid} this
61552          * @param {Number} rowIndex
61553          * @param {Number} columnIndex
61554          * @param {Roo.EventObject} e
61555          */
61556         "cellclick" : true,
61557         /**
61558          * @event celldblclick
61559          * Fires when a cell is double clicked
61560          * @param {Grid} this
61561          * @param {Number} rowIndex
61562          * @param {Number} columnIndex
61563          * @param {Roo.EventObject} e
61564          */
61565         "celldblclick" : true,
61566         /**
61567          * @event rowclick
61568          * Fires when a row is clicked
61569          * @param {Grid} this
61570          * @param {Number} rowIndex
61571          * @param {Roo.EventObject} e
61572          */
61573         "rowclick" : true,
61574         /**
61575          * @event rowdblclick
61576          * Fires when a row is double clicked
61577          * @param {Grid} this
61578          * @param {Number} rowIndex
61579          * @param {Roo.EventObject} e
61580          */
61581         "rowdblclick" : true,
61582         /**
61583          * @event headerclick
61584          * Fires when a header is clicked
61585          * @param {Grid} this
61586          * @param {Number} columnIndex
61587          * @param {Roo.EventObject} e
61588          */
61589         "headerclick" : true,
61590         /**
61591          * @event headerdblclick
61592          * Fires when a header cell is double clicked
61593          * @param {Grid} this
61594          * @param {Number} columnIndex
61595          * @param {Roo.EventObject} e
61596          */
61597         "headerdblclick" : true,
61598         /**
61599          * @event rowcontextmenu
61600          * Fires when a row is right clicked
61601          * @param {Grid} this
61602          * @param {Number} rowIndex
61603          * @param {Roo.EventObject} e
61604          */
61605         "rowcontextmenu" : true,
61606         /**
61607          * @event cellcontextmenu
61608          * Fires when a cell is right clicked
61609          * @param {Grid} this
61610          * @param {Number} rowIndex
61611          * @param {Number} cellIndex
61612          * @param {Roo.EventObject} e
61613          */
61614          "cellcontextmenu" : true,
61615         /**
61616          * @event headercontextmenu
61617          * Fires when a header is right clicked
61618          * @param {Grid} this
61619          * @param {Number} columnIndex
61620          * @param {Roo.EventObject} e
61621          */
61622         "headercontextmenu" : true,
61623         /**
61624          * @event bodyscroll
61625          * Fires when the body element is scrolled
61626          * @param {Number} scrollLeft
61627          * @param {Number} scrollTop
61628          */
61629         "bodyscroll" : true,
61630         /**
61631          * @event columnresize
61632          * Fires when the user resizes a column
61633          * @param {Number} columnIndex
61634          * @param {Number} newSize
61635          */
61636         "columnresize" : true,
61637         /**
61638          * @event columnmove
61639          * Fires when the user moves a column
61640          * @param {Number} oldIndex
61641          * @param {Number} newIndex
61642          */
61643         "columnmove" : true,
61644         /**
61645          * @event startdrag
61646          * Fires when row(s) start being dragged
61647          * @param {Grid} this
61648          * @param {Roo.GridDD} dd The drag drop object
61649          * @param {event} e The raw browser event
61650          */
61651         "startdrag" : true,
61652         /**
61653          * @event enddrag
61654          * Fires when a drag operation is complete
61655          * @param {Grid} this
61656          * @param {Roo.GridDD} dd The drag drop object
61657          * @param {event} e The raw browser event
61658          */
61659         "enddrag" : true,
61660         /**
61661          * @event dragdrop
61662          * Fires when dragged row(s) are dropped on a valid DD target
61663          * @param {Grid} this
61664          * @param {Roo.GridDD} dd The drag drop object
61665          * @param {String} targetId The target drag drop object
61666          * @param {event} e The raw browser event
61667          */
61668         "dragdrop" : true,
61669         /**
61670          * @event dragover
61671          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61672          * @param {Grid} this
61673          * @param {Roo.GridDD} dd The drag drop object
61674          * @param {String} targetId The target drag drop object
61675          * @param {event} e The raw browser event
61676          */
61677         "dragover" : true,
61678         /**
61679          * @event dragenter
61680          *  Fires when the dragged row(s) first cross another DD target while being dragged
61681          * @param {Grid} this
61682          * @param {Roo.GridDD} dd The drag drop object
61683          * @param {String} targetId The target drag drop object
61684          * @param {event} e The raw browser event
61685          */
61686         "dragenter" : true,
61687         /**
61688          * @event dragout
61689          * Fires when the dragged row(s) leave another DD target while being dragged
61690          * @param {Grid} this
61691          * @param {Roo.GridDD} dd The drag drop object
61692          * @param {String} targetId The target drag drop object
61693          * @param {event} e The raw browser event
61694          */
61695         "dragout" : true,
61696         /**
61697          * @event rowclass
61698          * Fires when a row is rendered, so you can change add a style to it.
61699          * @param {GridView} gridview   The grid view
61700          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61701          */
61702         'rowclass' : true,
61703
61704         /**
61705          * @event render
61706          * Fires when the grid is rendered
61707          * @param {Grid} grid
61708          */
61709         'render' : true,
61710             /**
61711              * @event select
61712              * Fires when a date is selected
61713              * @param {DatePicker} this
61714              * @param {Date} date The selected date
61715              */
61716         'select': true,
61717         /**
61718              * @event monthchange
61719              * Fires when the displayed month changes 
61720              * @param {DatePicker} this
61721              * @param {Date} date The selected month
61722              */
61723         'monthchange': true,
61724         /**
61725              * @event evententer
61726              * Fires when mouse over an event
61727              * @param {Calendar} this
61728              * @param {event} Event
61729              */
61730         'evententer': true,
61731         /**
61732              * @event eventleave
61733              * Fires when the mouse leaves an
61734              * @param {Calendar} this
61735              * @param {event}
61736              */
61737         'eventleave': true,
61738         /**
61739              * @event eventclick
61740              * Fires when the mouse click an
61741              * @param {Calendar} this
61742              * @param {event}
61743              */
61744         'eventclick': true,
61745         /**
61746              * @event eventrender
61747              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
61748              * @param {Calendar} this
61749              * @param {data} data to be modified
61750              */
61751         'eventrender': true
61752         
61753     });
61754
61755     Roo.grid.Grid.superclass.constructor.call(this);
61756     this.on('render', function() {
61757         this.view.el.addClass('x-grid-cal'); 
61758         
61759         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
61760
61761     },this);
61762     
61763     if (!Roo.grid.Calendar.style) {
61764         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
61765             
61766             
61767             '.x-grid-cal .x-grid-col' :  {
61768                 height: 'auto !important',
61769                 'vertical-align': 'top'
61770             },
61771             '.x-grid-cal  .fc-event-hori' : {
61772                 height: '14px'
61773             }
61774              
61775             
61776         }, Roo.id());
61777     }
61778
61779     
61780     
61781 };
61782 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
61783     /**
61784      * @cfg {Store} eventStore The store that loads events.
61785      */
61786     eventStore : 25,
61787
61788      
61789     activeDate : false,
61790     startDay : 0,
61791     autoWidth : true,
61792     monitorWindowResize : false,
61793
61794     
61795     resizeColumns : function() {
61796         var col = (this.view.el.getWidth() / 7) - 3;
61797         // loop through cols, and setWidth
61798         for(var i =0 ; i < 7 ; i++){
61799             this.cm.setColumnWidth(i, col);
61800         }
61801     },
61802      setDate :function(date) {
61803         
61804         Roo.log('setDate?');
61805         
61806         this.resizeColumns();
61807         var vd = this.activeDate;
61808         this.activeDate = date;
61809 //        if(vd && this.el){
61810 //            var t = date.getTime();
61811 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
61812 //                Roo.log('using add remove');
61813 //                
61814 //                this.fireEvent('monthchange', this, date);
61815 //                
61816 //                this.cells.removeClass("fc-state-highlight");
61817 //                this.cells.each(function(c){
61818 //                   if(c.dateValue == t){
61819 //                       c.addClass("fc-state-highlight");
61820 //                       setTimeout(function(){
61821 //                            try{c.dom.firstChild.focus();}catch(e){}
61822 //                       }, 50);
61823 //                       return false;
61824 //                   }
61825 //                   return true;
61826 //                });
61827 //                return;
61828 //            }
61829 //        }
61830         
61831         var days = date.getDaysInMonth();
61832         
61833         var firstOfMonth = date.getFirstDateOfMonth();
61834         var startingPos = firstOfMonth.getDay()-this.startDay;
61835         
61836         if(startingPos < this.startDay){
61837             startingPos += 7;
61838         }
61839         
61840         var pm = date.add(Date.MONTH, -1);
61841         var prevStart = pm.getDaysInMonth()-startingPos;
61842 //        
61843         
61844         
61845         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61846         
61847         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
61848         //this.cells.addClassOnOver('fc-state-hover');
61849         
61850         var cells = this.cells.elements;
61851         var textEls = this.textNodes;
61852         
61853         //Roo.each(cells, function(cell){
61854         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
61855         //});
61856         
61857         days += startingPos;
61858
61859         // convert everything to numbers so it's fast
61860         var day = 86400000;
61861         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
61862         //Roo.log(d);
61863         //Roo.log(pm);
61864         //Roo.log(prevStart);
61865         
61866         var today = new Date().clearTime().getTime();
61867         var sel = date.clearTime().getTime();
61868         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
61869         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
61870         var ddMatch = this.disabledDatesRE;
61871         var ddText = this.disabledDatesText;
61872         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
61873         var ddaysText = this.disabledDaysText;
61874         var format = this.format;
61875         
61876         var setCellClass = function(cal, cell){
61877             
61878             //Roo.log('set Cell Class');
61879             cell.title = "";
61880             var t = d.getTime();
61881             
61882             //Roo.log(d);
61883             
61884             
61885             cell.dateValue = t;
61886             if(t == today){
61887                 cell.className += " fc-today";
61888                 cell.className += " fc-state-highlight";
61889                 cell.title = cal.todayText;
61890             }
61891             if(t == sel){
61892                 // disable highlight in other month..
61893                 cell.className += " fc-state-highlight";
61894                 
61895             }
61896             // disabling
61897             if(t < min) {
61898                 //cell.className = " fc-state-disabled";
61899                 cell.title = cal.minText;
61900                 return;
61901             }
61902             if(t > max) {
61903                 //cell.className = " fc-state-disabled";
61904                 cell.title = cal.maxText;
61905                 return;
61906             }
61907             if(ddays){
61908                 if(ddays.indexOf(d.getDay()) != -1){
61909                     // cell.title = ddaysText;
61910                    // cell.className = " fc-state-disabled";
61911                 }
61912             }
61913             if(ddMatch && format){
61914                 var fvalue = d.dateFormat(format);
61915                 if(ddMatch.test(fvalue)){
61916                     cell.title = ddText.replace("%0", fvalue);
61917                    cell.className = " fc-state-disabled";
61918                 }
61919             }
61920             
61921             if (!cell.initialClassName) {
61922                 cell.initialClassName = cell.dom.className;
61923             }
61924             
61925             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
61926         };
61927
61928         var i = 0;
61929         
61930         for(; i < startingPos; i++) {
61931             cells[i].dayName =  (++prevStart);
61932             Roo.log(textEls[i]);
61933             d.setDate(d.getDate()+1);
61934             
61935             //cells[i].className = "fc-past fc-other-month";
61936             setCellClass(this, cells[i]);
61937         }
61938         
61939         var intDay = 0;
61940         
61941         for(; i < days; i++){
61942             intDay = i - startingPos + 1;
61943             cells[i].dayName =  (intDay);
61944             d.setDate(d.getDate()+1);
61945             
61946             cells[i].className = ''; // "x-date-active";
61947             setCellClass(this, cells[i]);
61948         }
61949         var extraDays = 0;
61950         
61951         for(; i < 42; i++) {
61952             //textEls[i].innerHTML = (++extraDays);
61953             
61954             d.setDate(d.getDate()+1);
61955             cells[i].dayName = (++extraDays);
61956             cells[i].className = "fc-future fc-other-month";
61957             setCellClass(this, cells[i]);
61958         }
61959         
61960         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
61961         
61962         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
61963         
61964         // this will cause all the cells to mis
61965         var rows= [];
61966         var i =0;
61967         for (var r = 0;r < 6;r++) {
61968             for (var c =0;c < 7;c++) {
61969                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
61970             }    
61971         }
61972         
61973         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61974         for(i=0;i<cells.length;i++) {
61975             
61976             this.cells.elements[i].dayName = cells[i].dayName ;
61977             this.cells.elements[i].className = cells[i].className;
61978             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
61979             this.cells.elements[i].title = cells[i].title ;
61980             this.cells.elements[i].dateValue = cells[i].dateValue ;
61981         }
61982         
61983         
61984         
61985         
61986         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
61987         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
61988         
61989         ////if(totalRows != 6){
61990             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
61991            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
61992        // }
61993         
61994         this.fireEvent('monthchange', this, date);
61995         
61996         
61997     },
61998  /**
61999      * Returns the grid's SelectionModel.
62000      * @return {SelectionModel}
62001      */
62002     getSelectionModel : function(){
62003         if(!this.selModel){
62004             this.selModel = new Roo.grid.CellSelectionModel();
62005         }
62006         return this.selModel;
62007     },
62008
62009     load: function() {
62010         this.eventStore.load()
62011         
62012         
62013         
62014     },
62015     
62016     findCell : function(dt) {
62017         dt = dt.clearTime().getTime();
62018         var ret = false;
62019         this.cells.each(function(c){
62020             //Roo.log("check " +c.dateValue + '?=' + dt);
62021             if(c.dateValue == dt){
62022                 ret = c;
62023                 return false;
62024             }
62025             return true;
62026         });
62027         
62028         return ret;
62029     },
62030     
62031     findCells : function(rec) {
62032         var s = rec.data.start_dt.clone().clearTime().getTime();
62033        // Roo.log(s);
62034         var e= rec.data.end_dt.clone().clearTime().getTime();
62035        // Roo.log(e);
62036         var ret = [];
62037         this.cells.each(function(c){
62038              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
62039             
62040             if(c.dateValue > e){
62041                 return ;
62042             }
62043             if(c.dateValue < s){
62044                 return ;
62045             }
62046             ret.push(c);
62047         });
62048         
62049         return ret;    
62050     },
62051     
62052     findBestRow: function(cells)
62053     {
62054         var ret = 0;
62055         
62056         for (var i =0 ; i < cells.length;i++) {
62057             ret  = Math.max(cells[i].rows || 0,ret);
62058         }
62059         return ret;
62060         
62061     },
62062     
62063     
62064     addItem : function(rec)
62065     {
62066         // look for vertical location slot in
62067         var cells = this.findCells(rec);
62068         
62069         rec.row = this.findBestRow(cells);
62070         
62071         // work out the location.
62072         
62073         var crow = false;
62074         var rows = [];
62075         for(var i =0; i < cells.length; i++) {
62076             if (!crow) {
62077                 crow = {
62078                     start : cells[i],
62079                     end :  cells[i]
62080                 };
62081                 continue;
62082             }
62083             if (crow.start.getY() == cells[i].getY()) {
62084                 // on same row.
62085                 crow.end = cells[i];
62086                 continue;
62087             }
62088             // different row.
62089             rows.push(crow);
62090             crow = {
62091                 start: cells[i],
62092                 end : cells[i]
62093             };
62094             
62095         }
62096         
62097         rows.push(crow);
62098         rec.els = [];
62099         rec.rows = rows;
62100         rec.cells = cells;
62101         for (var i = 0; i < cells.length;i++) {
62102             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
62103             
62104         }
62105         
62106         
62107     },
62108     
62109     clearEvents: function() {
62110         
62111         if (!this.eventStore.getCount()) {
62112             return;
62113         }
62114         // reset number of rows in cells.
62115         Roo.each(this.cells.elements, function(c){
62116             c.rows = 0;
62117         });
62118         
62119         this.eventStore.each(function(e) {
62120             this.clearEvent(e);
62121         },this);
62122         
62123     },
62124     
62125     clearEvent : function(ev)
62126     {
62127         if (ev.els) {
62128             Roo.each(ev.els, function(el) {
62129                 el.un('mouseenter' ,this.onEventEnter, this);
62130                 el.un('mouseleave' ,this.onEventLeave, this);
62131                 el.remove();
62132             },this);
62133             ev.els = [];
62134         }
62135     },
62136     
62137     
62138     renderEvent : function(ev,ctr) {
62139         if (!ctr) {
62140              ctr = this.view.el.select('.fc-event-container',true).first();
62141         }
62142         
62143          
62144         this.clearEvent(ev);
62145             //code
62146        
62147         
62148         
62149         ev.els = [];
62150         var cells = ev.cells;
62151         var rows = ev.rows;
62152         this.fireEvent('eventrender', this, ev);
62153         
62154         for(var i =0; i < rows.length; i++) {
62155             
62156             cls = '';
62157             if (i == 0) {
62158                 cls += ' fc-event-start';
62159             }
62160             if ((i+1) == rows.length) {
62161                 cls += ' fc-event-end';
62162             }
62163             
62164             //Roo.log(ev.data);
62165             // how many rows should it span..
62166             var cg = this.eventTmpl.append(ctr,Roo.apply({
62167                 fccls : cls
62168                 
62169             }, ev.data) , true);
62170             
62171             
62172             cg.on('mouseenter' ,this.onEventEnter, this, ev);
62173             cg.on('mouseleave' ,this.onEventLeave, this, ev);
62174             cg.on('click', this.onEventClick, this, ev);
62175             
62176             ev.els.push(cg);
62177             
62178             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
62179             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
62180             //Roo.log(cg);
62181              
62182             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
62183             cg.setWidth(ebox.right - sbox.x -2);
62184         }
62185     },
62186     
62187     renderEvents: function()
62188     {   
62189         // first make sure there is enough space..
62190         
62191         if (!this.eventTmpl) {
62192             this.eventTmpl = new Roo.Template(
62193                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
62194                     '<div class="fc-event-inner">' +
62195                         '<span class="fc-event-time">{time}</span>' +
62196                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
62197                     '</div>' +
62198                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
62199                 '</div>'
62200             );
62201                 
62202         }
62203                
62204         
62205         
62206         this.cells.each(function(c) {
62207             //Roo.log(c.select('.fc-day-content div',true).first());
62208             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
62209         });
62210         
62211         var ctr = this.view.el.select('.fc-event-container',true).first();
62212         
62213         var cls;
62214         this.eventStore.each(function(ev){
62215             
62216             this.renderEvent(ev);
62217              
62218              
62219         }, this);
62220         this.view.layout();
62221         
62222     },
62223     
62224     onEventEnter: function (e, el,event,d) {
62225         this.fireEvent('evententer', this, el, event);
62226     },
62227     
62228     onEventLeave: function (e, el,event,d) {
62229         this.fireEvent('eventleave', this, el, event);
62230     },
62231     
62232     onEventClick: function (e, el,event,d) {
62233         this.fireEvent('eventclick', this, el, event);
62234     },
62235     
62236     onMonthChange: function () {
62237         this.store.load();
62238     },
62239     
62240     onLoad: function () {
62241         
62242         //Roo.log('calendar onload');
62243 //         
62244         if(this.eventStore.getCount() > 0){
62245             
62246            
62247             
62248             this.eventStore.each(function(d){
62249                 
62250                 
62251                 // FIXME..
62252                 var add =   d.data;
62253                 if (typeof(add.end_dt) == 'undefined')  {
62254                     Roo.log("Missing End time in calendar data: ");
62255                     Roo.log(d);
62256                     return;
62257                 }
62258                 if (typeof(add.start_dt) == 'undefined')  {
62259                     Roo.log("Missing Start time in calendar data: ");
62260                     Roo.log(d);
62261                     return;
62262                 }
62263                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
62264                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
62265                 add.id = add.id || d.id;
62266                 add.title = add.title || '??';
62267                 
62268                 this.addItem(d);
62269                 
62270              
62271             },this);
62272         }
62273         
62274         this.renderEvents();
62275     }
62276     
62277
62278 });
62279 /*
62280  grid : {
62281                 xtype: 'Grid',
62282                 xns: Roo.grid,
62283                 listeners : {
62284                     render : function ()
62285                     {
62286                         _this.grid = this;
62287                         
62288                         if (!this.view.el.hasClass('course-timesheet')) {
62289                             this.view.el.addClass('course-timesheet');
62290                         }
62291                         if (this.tsStyle) {
62292                             this.ds.load({});
62293                             return; 
62294                         }
62295                         Roo.log('width');
62296                         Roo.log(_this.grid.view.el.getWidth());
62297                         
62298                         
62299                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62300                             '.course-timesheet .x-grid-row' : {
62301                                 height: '80px'
62302                             },
62303                             '.x-grid-row td' : {
62304                                 'vertical-align' : 0
62305                             },
62306                             '.course-edit-link' : {
62307                                 'color' : 'blue',
62308                                 'text-overflow' : 'ellipsis',
62309                                 'overflow' : 'hidden',
62310                                 'white-space' : 'nowrap',
62311                                 'cursor' : 'pointer'
62312                             },
62313                             '.sub-link' : {
62314                                 'color' : 'green'
62315                             },
62316                             '.de-act-sup-link' : {
62317                                 'color' : 'purple',
62318                                 'text-decoration' : 'line-through'
62319                             },
62320                             '.de-act-link' : {
62321                                 'color' : 'red',
62322                                 'text-decoration' : 'line-through'
62323                             },
62324                             '.course-timesheet .course-highlight' : {
62325                                 'border-top-style': 'dashed !important',
62326                                 'border-bottom-bottom': 'dashed !important'
62327                             },
62328                             '.course-timesheet .course-item' : {
62329                                 'font-family'   : 'tahoma, arial, helvetica',
62330                                 'font-size'     : '11px',
62331                                 'overflow'      : 'hidden',
62332                                 'padding-left'  : '10px',
62333                                 'padding-right' : '10px',
62334                                 'padding-top' : '10px' 
62335                             }
62336                             
62337                         }, Roo.id());
62338                                 this.ds.load({});
62339                     }
62340                 },
62341                 autoWidth : true,
62342                 monitorWindowResize : false,
62343                 cellrenderer : function(v,x,r)
62344                 {
62345                     return v;
62346                 },
62347                 sm : {
62348                     xtype: 'CellSelectionModel',
62349                     xns: Roo.grid
62350                 },
62351                 dataSource : {
62352                     xtype: 'Store',
62353                     xns: Roo.data,
62354                     listeners : {
62355                         beforeload : function (_self, options)
62356                         {
62357                             options.params = options.params || {};
62358                             options.params._month = _this.monthField.getValue();
62359                             options.params.limit = 9999;
62360                             options.params['sort'] = 'when_dt';    
62361                             options.params['dir'] = 'ASC';    
62362                             this.proxy.loadResponse = this.loadResponse;
62363                             Roo.log("load?");
62364                             //this.addColumns();
62365                         },
62366                         load : function (_self, records, options)
62367                         {
62368                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
62369                                 // if you click on the translation.. you can edit it...
62370                                 var el = Roo.get(this);
62371                                 var id = el.dom.getAttribute('data-id');
62372                                 var d = el.dom.getAttribute('data-date');
62373                                 var t = el.dom.getAttribute('data-time');
62374                                 //var id = this.child('span').dom.textContent;
62375                                 
62376                                 //Roo.log(this);
62377                                 Pman.Dialog.CourseCalendar.show({
62378                                     id : id,
62379                                     when_d : d,
62380                                     when_t : t,
62381                                     productitem_active : id ? 1 : 0
62382                                 }, function() {
62383                                     _this.grid.ds.load({});
62384                                 });
62385                            
62386                            });
62387                            
62388                            _this.panel.fireEvent('resize', [ '', '' ]);
62389                         }
62390                     },
62391                     loadResponse : function(o, success, response){
62392                             // this is overridden on before load..
62393                             
62394                             Roo.log("our code?");       
62395                             //Roo.log(success);
62396                             //Roo.log(response)
62397                             delete this.activeRequest;
62398                             if(!success){
62399                                 this.fireEvent("loadexception", this, o, response);
62400                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62401                                 return;
62402                             }
62403                             var result;
62404                             try {
62405                                 result = o.reader.read(response);
62406                             }catch(e){
62407                                 Roo.log("load exception?");
62408                                 this.fireEvent("loadexception", this, o, response, e);
62409                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62410                                 return;
62411                             }
62412                             Roo.log("ready...");        
62413                             // loop through result.records;
62414                             // and set this.tdate[date] = [] << array of records..
62415                             _this.tdata  = {};
62416                             Roo.each(result.records, function(r){
62417                                 //Roo.log(r.data);
62418                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
62419                                     _this.tdata[r.data.when_dt.format('j')] = [];
62420                                 }
62421                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
62422                             });
62423                             
62424                             //Roo.log(_this.tdata);
62425                             
62426                             result.records = [];
62427                             result.totalRecords = 6;
62428                     
62429                             // let's generate some duumy records for the rows.
62430                             //var st = _this.dateField.getValue();
62431                             
62432                             // work out monday..
62433                             //st = st.add(Date.DAY, -1 * st.format('w'));
62434                             
62435                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62436                             
62437                             var firstOfMonth = date.getFirstDayOfMonth();
62438                             var days = date.getDaysInMonth();
62439                             var d = 1;
62440                             var firstAdded = false;
62441                             for (var i = 0; i < result.totalRecords ; i++) {
62442                                 //var d= st.add(Date.DAY, i);
62443                                 var row = {};
62444                                 var added = 0;
62445                                 for(var w = 0 ; w < 7 ; w++){
62446                                     if(!firstAdded && firstOfMonth != w){
62447                                         continue;
62448                                     }
62449                                     if(d > days){
62450                                         continue;
62451                                     }
62452                                     firstAdded = true;
62453                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
62454                                     row['weekday'+w] = String.format(
62455                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
62456                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
62457                                                     d,
62458                                                     date.format('Y-m-')+dd
62459                                                 );
62460                                     added++;
62461                                     if(typeof(_this.tdata[d]) != 'undefined'){
62462                                         Roo.each(_this.tdata[d], function(r){
62463                                             var is_sub = '';
62464                                             var deactive = '';
62465                                             var id = r.id;
62466                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
62467                                             if(r.parent_id*1>0){
62468                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
62469                                                 id = r.parent_id;
62470                                             }
62471                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
62472                                                 deactive = 'de-act-link';
62473                                             }
62474                                             
62475                                             row['weekday'+w] += String.format(
62476                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
62477                                                     id, //0
62478                                                     r.product_id_name, //1
62479                                                     r.when_dt.format('h:ia'), //2
62480                                                     is_sub, //3
62481                                                     deactive, //4
62482                                                     desc // 5
62483                                             );
62484                                         });
62485                                     }
62486                                     d++;
62487                                 }
62488                                 
62489                                 // only do this if something added..
62490                                 if(added > 0){ 
62491                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
62492                                 }
62493                                 
62494                                 
62495                                 // push it twice. (second one with an hour..
62496                                 
62497                             }
62498                             //Roo.log(result);
62499                             this.fireEvent("load", this, o, o.request.arg);
62500                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
62501                         },
62502                     sortInfo : {field: 'when_dt', direction : 'ASC' },
62503                     proxy : {
62504                         xtype: 'HttpProxy',
62505                         xns: Roo.data,
62506                         method : 'GET',
62507                         url : baseURL + '/Roo/Shop_course.php'
62508                     },
62509                     reader : {
62510                         xtype: 'JsonReader',
62511                         xns: Roo.data,
62512                         id : 'id',
62513                         fields : [
62514                             {
62515                                 'name': 'id',
62516                                 'type': 'int'
62517                             },
62518                             {
62519                                 'name': 'when_dt',
62520                                 'type': 'string'
62521                             },
62522                             {
62523                                 'name': 'end_dt',
62524                                 'type': 'string'
62525                             },
62526                             {
62527                                 'name': 'parent_id',
62528                                 'type': 'int'
62529                             },
62530                             {
62531                                 'name': 'product_id',
62532                                 'type': 'int'
62533                             },
62534                             {
62535                                 'name': 'productitem_id',
62536                                 'type': 'int'
62537                             },
62538                             {
62539                                 'name': 'guid',
62540                                 'type': 'int'
62541                             }
62542                         ]
62543                     }
62544                 },
62545                 toolbar : {
62546                     xtype: 'Toolbar',
62547                     xns: Roo,
62548                     items : [
62549                         {
62550                             xtype: 'Button',
62551                             xns: Roo.Toolbar,
62552                             listeners : {
62553                                 click : function (_self, e)
62554                                 {
62555                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62556                                     sd.setMonth(sd.getMonth()-1);
62557                                     _this.monthField.setValue(sd.format('Y-m-d'));
62558                                     _this.grid.ds.load({});
62559                                 }
62560                             },
62561                             text : "Back"
62562                         },
62563                         {
62564                             xtype: 'Separator',
62565                             xns: Roo.Toolbar
62566                         },
62567                         {
62568                             xtype: 'MonthField',
62569                             xns: Roo.form,
62570                             listeners : {
62571                                 render : function (_self)
62572                                 {
62573                                     _this.monthField = _self;
62574                                    // _this.monthField.set  today
62575                                 },
62576                                 select : function (combo, date)
62577                                 {
62578                                     _this.grid.ds.load({});
62579                                 }
62580                             },
62581                             value : (function() { return new Date(); })()
62582                         },
62583                         {
62584                             xtype: 'Separator',
62585                             xns: Roo.Toolbar
62586                         },
62587                         {
62588                             xtype: 'TextItem',
62589                             xns: Roo.Toolbar,
62590                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
62591                         },
62592                         {
62593                             xtype: 'Fill',
62594                             xns: Roo.Toolbar
62595                         },
62596                         {
62597                             xtype: 'Button',
62598                             xns: Roo.Toolbar,
62599                             listeners : {
62600                                 click : function (_self, e)
62601                                 {
62602                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62603                                     sd.setMonth(sd.getMonth()+1);
62604                                     _this.monthField.setValue(sd.format('Y-m-d'));
62605                                     _this.grid.ds.load({});
62606                                 }
62607                             },
62608                             text : "Next"
62609                         }
62610                     ]
62611                 },
62612                  
62613             }
62614         };
62615         
62616         *//*
62617  * Based on:
62618  * Ext JS Library 1.1.1
62619  * Copyright(c) 2006-2007, Ext JS, LLC.
62620  *
62621  * Originally Released Under LGPL - original licence link has changed is not relivant.
62622  *
62623  * Fork - LGPL
62624  * <script type="text/javascript">
62625  */
62626  
62627 /**
62628  * @class Roo.LoadMask
62629  * A simple utility class for generically masking elements while loading data.  If the element being masked has
62630  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
62631  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
62632  * element's UpdateManager load indicator and will be destroyed after the initial load.
62633  * @constructor
62634  * Create a new LoadMask
62635  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
62636  * @param {Object} config The config object
62637  */
62638 Roo.LoadMask = function(el, config){
62639     this.el = Roo.get(el);
62640     Roo.apply(this, config);
62641     if(this.store){
62642         this.store.on('beforeload', this.onBeforeLoad, this);
62643         this.store.on('load', this.onLoad, this);
62644         this.store.on('loadexception', this.onLoadException, this);
62645         this.removeMask = false;
62646     }else{
62647         var um = this.el.getUpdateManager();
62648         um.showLoadIndicator = false; // disable the default indicator
62649         um.on('beforeupdate', this.onBeforeLoad, this);
62650         um.on('update', this.onLoad, this);
62651         um.on('failure', this.onLoad, this);
62652         this.removeMask = true;
62653     }
62654 };
62655
62656 Roo.LoadMask.prototype = {
62657     /**
62658      * @cfg {Boolean} removeMask
62659      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
62660      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
62661      */
62662     removeMask : false,
62663     /**
62664      * @cfg {String} msg
62665      * The text to display in a centered loading message box (defaults to 'Loading...')
62666      */
62667     msg : 'Loading...',
62668     /**
62669      * @cfg {String} msgCls
62670      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
62671      */
62672     msgCls : 'x-mask-loading',
62673
62674     /**
62675      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
62676      * @type Boolean
62677      */
62678     disabled: false,
62679
62680     /**
62681      * Disables the mask to prevent it from being displayed
62682      */
62683     disable : function(){
62684        this.disabled = true;
62685     },
62686
62687     /**
62688      * Enables the mask so that it can be displayed
62689      */
62690     enable : function(){
62691         this.disabled = false;
62692     },
62693     
62694     onLoadException : function()
62695     {
62696         Roo.log(arguments);
62697         
62698         if (typeof(arguments[3]) != 'undefined') {
62699             Roo.MessageBox.alert("Error loading",arguments[3]);
62700         } 
62701         /*
62702         try {
62703             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
62704                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
62705             }   
62706         } catch(e) {
62707             
62708         }
62709         */
62710     
62711         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62712     },
62713     // private
62714     onLoad : function()
62715     {
62716         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62717     },
62718
62719     // private
62720     onBeforeLoad : function(){
62721         if(!this.disabled){
62722             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
62723         }
62724     },
62725
62726     // private
62727     destroy : function(){
62728         if(this.store){
62729             this.store.un('beforeload', this.onBeforeLoad, this);
62730             this.store.un('load', this.onLoad, this);
62731             this.store.un('loadexception', this.onLoadException, this);
62732         }else{
62733             var um = this.el.getUpdateManager();
62734             um.un('beforeupdate', this.onBeforeLoad, this);
62735             um.un('update', this.onLoad, this);
62736             um.un('failure', this.onLoad, this);
62737         }
62738     }
62739 };/*
62740  * Based on:
62741  * Ext JS Library 1.1.1
62742  * Copyright(c) 2006-2007, Ext JS, LLC.
62743  *
62744  * Originally Released Under LGPL - original licence link has changed is not relivant.
62745  *
62746  * Fork - LGPL
62747  * <script type="text/javascript">
62748  */
62749
62750
62751 /**
62752  * @class Roo.XTemplate
62753  * @extends Roo.Template
62754  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
62755 <pre><code>
62756 var t = new Roo.XTemplate(
62757         '&lt;select name="{name}"&gt;',
62758                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
62759         '&lt;/select&gt;'
62760 );
62761  
62762 // then append, applying the master template values
62763  </code></pre>
62764  *
62765  * Supported features:
62766  *
62767  *  Tags:
62768
62769 <pre><code>
62770       {a_variable} - output encoded.
62771       {a_variable.format:("Y-m-d")} - call a method on the variable
62772       {a_variable:raw} - unencoded output
62773       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
62774       {a_variable:this.method_on_template(...)} - call a method on the template object.
62775  
62776 </code></pre>
62777  *  The tpl tag:
62778 <pre><code>
62779         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
62780         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
62781         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
62782         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
62783   
62784         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
62785         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
62786 </code></pre>
62787  *      
62788  */
62789 Roo.XTemplate = function()
62790 {
62791     Roo.XTemplate.superclass.constructor.apply(this, arguments);
62792     if (this.html) {
62793         this.compile();
62794     }
62795 };
62796
62797
62798 Roo.extend(Roo.XTemplate, Roo.Template, {
62799
62800     /**
62801      * The various sub templates
62802      */
62803     tpls : false,
62804     /**
62805      *
62806      * basic tag replacing syntax
62807      * WORD:WORD()
62808      *
62809      * // you can fake an object call by doing this
62810      *  x.t:(test,tesT) 
62811      * 
62812      */
62813     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
62814
62815     /**
62816      * compile the template
62817      *
62818      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
62819      *
62820      */
62821     compile: function()
62822     {
62823         var s = this.html;
62824      
62825         s = ['<tpl>', s, '</tpl>'].join('');
62826     
62827         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
62828             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
62829             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
62830             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
62831             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
62832             m,
62833             id     = 0,
62834             tpls   = [];
62835     
62836         while(true == !!(m = s.match(re))){
62837             var forMatch   = m[0].match(nameRe),
62838                 ifMatch   = m[0].match(ifRe),
62839                 execMatch   = m[0].match(execRe),
62840                 namedMatch   = m[0].match(namedRe),
62841                 
62842                 exp  = null, 
62843                 fn   = null,
62844                 exec = null,
62845                 name = forMatch && forMatch[1] ? forMatch[1] : '';
62846                 
62847             if (ifMatch) {
62848                 // if - puts fn into test..
62849                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
62850                 if(exp){
62851                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
62852                 }
62853             }
62854             
62855             if (execMatch) {
62856                 // exec - calls a function... returns empty if true is  returned.
62857                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
62858                 if(exp){
62859                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
62860                 }
62861             }
62862             
62863             
62864             if (name) {
62865                 // for = 
62866                 switch(name){
62867                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
62868                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
62869                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
62870                 }
62871             }
62872             var uid = namedMatch ? namedMatch[1] : id;
62873             
62874             
62875             tpls.push({
62876                 id:     namedMatch ? namedMatch[1] : id,
62877                 target: name,
62878                 exec:   exec,
62879                 test:   fn,
62880                 body:   m[1] || ''
62881             });
62882             if (namedMatch) {
62883                 s = s.replace(m[0], '');
62884             } else { 
62885                 s = s.replace(m[0], '{xtpl'+ id + '}');
62886             }
62887             ++id;
62888         }
62889         this.tpls = [];
62890         for(var i = tpls.length-1; i >= 0; --i){
62891             this.compileTpl(tpls[i]);
62892             this.tpls[tpls[i].id] = tpls[i];
62893         }
62894         this.master = tpls[tpls.length-1];
62895         return this;
62896     },
62897     /**
62898      * same as applyTemplate, except it's done to one of the subTemplates
62899      * when using named templates, you can do:
62900      *
62901      * var str = pl.applySubTemplate('your-name', values);
62902      *
62903      * 
62904      * @param {Number} id of the template
62905      * @param {Object} values to apply to template
62906      * @param {Object} parent (normaly the instance of this object)
62907      */
62908     applySubTemplate : function(id, values, parent)
62909     {
62910         
62911         
62912         var t = this.tpls[id];
62913         
62914         
62915         try { 
62916             if(t.test && !t.test.call(this, values, parent)){
62917                 return '';
62918             }
62919         } catch(e) {
62920             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
62921             Roo.log(e.toString());
62922             Roo.log(t.test);
62923             return ''
62924         }
62925         try { 
62926             
62927             if(t.exec && t.exec.call(this, values, parent)){
62928                 return '';
62929             }
62930         } catch(e) {
62931             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
62932             Roo.log(e.toString());
62933             Roo.log(t.exec);
62934             return ''
62935         }
62936         try {
62937             var vs = t.target ? t.target.call(this, values, parent) : values;
62938             parent = t.target ? values : parent;
62939             if(t.target && vs instanceof Array){
62940                 var buf = [];
62941                 for(var i = 0, len = vs.length; i < len; i++){
62942                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
62943                 }
62944                 return buf.join('');
62945             }
62946             return t.compiled.call(this, vs, parent);
62947         } catch (e) {
62948             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
62949             Roo.log(e.toString());
62950             Roo.log(t.compiled);
62951             return '';
62952         }
62953     },
62954
62955     compileTpl : function(tpl)
62956     {
62957         var fm = Roo.util.Format;
62958         var useF = this.disableFormats !== true;
62959         var sep = Roo.isGecko ? "+" : ",";
62960         var undef = function(str) {
62961             Roo.log("Property not found :"  + str);
62962             return '';
62963         };
62964         
62965         var fn = function(m, name, format, args)
62966         {
62967             //Roo.log(arguments);
62968             args = args ? args.replace(/\\'/g,"'") : args;
62969             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
62970             if (typeof(format) == 'undefined') {
62971                 format= 'htmlEncode';
62972             }
62973             if (format == 'raw' ) {
62974                 format = false;
62975             }
62976             
62977             if(name.substr(0, 4) == 'xtpl'){
62978                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
62979             }
62980             
62981             // build an array of options to determine if value is undefined..
62982             
62983             // basically get 'xxxx.yyyy' then do
62984             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
62985             //    (function () { Roo.log("Property not found"); return ''; })() :
62986             //    ......
62987             
62988             var udef_ar = [];
62989             var lookfor = '';
62990             Roo.each(name.split('.'), function(st) {
62991                 lookfor += (lookfor.length ? '.': '') + st;
62992                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
62993             });
62994             
62995             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
62996             
62997             
62998             if(format && useF){
62999                 
63000                 args = args ? ',' + args : "";
63001                  
63002                 if(format.substr(0, 5) != "this."){
63003                     format = "fm." + format + '(';
63004                 }else{
63005                     format = 'this.call("'+ format.substr(5) + '", ';
63006                     args = ", values";
63007                 }
63008                 
63009                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
63010             }
63011              
63012             if (args.length) {
63013                 // called with xxyx.yuu:(test,test)
63014                 // change to ()
63015                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
63016             }
63017             // raw.. - :raw modifier..
63018             return "'"+ sep + udef_st  + name + ")"+sep+"'";
63019             
63020         };
63021         var body;
63022         // branched to use + in gecko and [].join() in others
63023         if(Roo.isGecko){
63024             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
63025                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
63026                     "';};};";
63027         }else{
63028             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
63029             body.push(tpl.body.replace(/(\r\n|\n)/g,
63030                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
63031             body.push("'].join('');};};");
63032             body = body.join('');
63033         }
63034         
63035         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
63036        
63037         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
63038         eval(body);
63039         
63040         return this;
63041     },
63042
63043     applyTemplate : function(values){
63044         return this.master.compiled.call(this, values, {});
63045         //var s = this.subs;
63046     },
63047
63048     apply : function(){
63049         return this.applyTemplate.apply(this, arguments);
63050     }
63051
63052  });
63053
63054 Roo.XTemplate.from = function(el){
63055     el = Roo.getDom(el);
63056     return new Roo.XTemplate(el.value || el.innerHTML);
63057 };